#358 stale
nerdrew

find with a has_many condition fails

Reported by nerdrew | June 7th, 2008 @ 01:06 AM

Given a model with a has_many that has a condition, a find with this has_many association included will fail.

class Company < ActiveRecord::Base
  has_many(
      :employments, 
      :class_name => 'Employment', 
      :conditions => {
        :end_date => nil
      },
      :include => :person
    )
end

class Employment < ActiveRecord::Base
  belongs_to :person
  belongs_to :company
end

class Person < ActiveRecord::Base
end

Company.find(:all, :include => {:employments => :person})
Company.find(:all, :include => :employments)

The last two statements both fail. I've attached a sample that fails for me.

The problem is that the condition for the has_many tries to use companies.end_date for the condition instead of employments.end_date

The error:

ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: companies.end_date

Comments and changes to this ticket

  • nerdrew
  • nerdrew

    nerdrew June 7th, 2008 @ 01:11 AM

    I can't attach files without an error, so I've uploaded it here:

    http://www.nerdputer.com/test_ra...

  • Greg Campbell

    Greg Campbell June 7th, 2008 @ 01:52 AM

    The same is true for a has_one association with a condition.

    I have:

    class Contact < ActiveRecord::Base
      has_one :primary_email, :class_name => 'ContactEmail', :conditions => {:primary_email => true}
    end
    
    Contact.find(:first).primary_email #=> #<ContactEmail ... >
    Contact.find(:first, :include => :primary_email) #=> error
    

    The error in question is ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'contacts.primary_email' in 'where clause': SELECT `contact_emails`.* FROM `contact_emails` WHERE (`contact_emails`.contact_id IN (1) AND (`contacts`.`primary_email` = 1))

  • Joshua Peek

    Joshua Peek September 18th, 2008 @ 03:49 PM

    • → Tag cleared.
    • → State changed from “new” to “stale”
  • nerdrew

    nerdrew September 18th, 2008 @ 08:03 PM

    Why is this stale now? I'm still seeing the same bug (I think) in edge. It could be that I'm using the conditions hash wrong, but I don't know how else I'd do it. My understanding is that the conditions hash of a has_many should be properties on the associated model. So, in the example above, the {:end_date => nil} should apply to the employment model, not the company model.

  • Tarmo Tänav

    Tarmo Tänav September 18th, 2008 @ 09:24 PM

    stale only means that the bug is not moving anywhere, either no patch for a long time or the patch no longer applies.

  • nerdrew

    nerdrew September 19th, 2008 @ 03:21 AM

    Can someone test this for me? I have some custom ActiveRecord improvements (nested condition hashes and includes), but it seems to work for me.

    @@@ruby

    class <<ActiveRecord::Base

    private

    def find_associated_records(ids, reflection, preload_options)

    options = reflection.options
    table_name = reflection.klass.quoted_table_name
    
    if !options[:conditions].is_a?(Hash) && !preload_options[:conditions].is_a?(Hash)
      if interface = reflection.options[:as]
        conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
      else
        foreign_key = reflection.primary_key_name
        conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"
      end
    
      conditions << append_conditions(options, preload_options)
      conditions = [conditions, ids]
    else
      conditions = if interface = reflection.options[:as]
        {"#{interface}_id" => ids, "#{interface}_type" => self.base_class.sti_name}
      else
        {reflection.primary_key_name => ids}
      end
    
      conditions.merge!(options[:conditions]) if options[:conditions]
      conditions.merge!(preload_options[:conditions]) if preload_options[:conditions]
    end
    
    reflection.klass.find(:all,
                          :select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
                          :include => preload_options[:include] || options[:include],
                          :conditions => conditions,
                          :joins => options[:joins],
                          :group => preload_options[:group] || options[:group],
                          :order => preload_options[:order] || options[:order])
    
    

    end

    end #adding to ActiveRecord::Base

    
    
  • nerdrew

    nerdrew September 19th, 2008 @ 03:22 AM

    I wish there was a way to edit comments...

    Here it is again.

    
    
    class <<ActiveRecord::Base
    
      private
    
      def find_associated_records(ids, reflection, preload_options)
        options = reflection.options
        table_name = reflection.klass.quoted_table_name
    
        if !options[:conditions].is_a?(Hash) && !preload_options[:conditions].is_a?(Hash)
          if interface = reflection.options[:as]
            conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
          else
            foreign_key = reflection.primary_key_name
            conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"
          end
    
          conditions << append_conditions(options, preload_options)
          conditions = [conditions, ids]
        else
          conditions = if interface = reflection.options[:as]
            {"#{interface}_id" => ids, "#{interface}_type" => self.base_class.sti_name}
          else
            {reflection.primary_key_name => ids}
          end
    
          conditions.merge!(options[:conditions]) if options[:conditions]
          conditions.merge!(preload_options[:conditions]) if preload_options[:conditions]
        end
    
        reflection.klass.find(:all,
                              :select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
                              :include => preload_options[:include] || options[:include],
                              :conditions => conditions,
                              :joins => options[:joins],
                              :group => preload_options[:group] || options[:group],
                              :order => preload_options[:order] || options[:order])
      end
    
    end #adding to ActiveRecord::Base
    
    

Please Login or create a free account to add a new comment.

You can update this ticket by sending an email to from your email client. (help)

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

Source available from github

The Git repository resides at http://github.com/rails

Check out the current development trunk (Edge Rails) with:

git clone git://github.com/rails/rails.git

Creating or reviewing a patch

See the contributor guide.

Creating a feature request

Please don't. If you want a new feature in Rails, you'll have to pull up your sleeves and get busy yourself. Or convince someone else to do it. See the contributor guide on how to get going. But posting them here is just going to lead to ticket root.

Creating a bug report

When creating a bug report, be sure to include as much relevant information as possible. Post the code sample that causes the problem. Preferably, alter the unit tests and show through either changed or added tests how the expected behavior is not occuring.

Security vulnerabilities should be reported via an email to security@rubyonrails.org, do not use trac for reporting security vulnerabilities. All content in trac is publicly available as soon as it is posted.

Then don't get your hopes up. Unless you have a "Code Red, Mission Critical, The World is Coming to an End" kinda bug, you're creating this ticket in the hope that others with the same problem will be able to collaborate with you on solving it. Do not expect that the ticket automatically will see any activity or that others will jump to fix it. Creating a ticket like this is mostly to help yourself start on the path of fixing the problem and for others to sign on to with a "I'm having this problem too".

Shared Ticket Bins