简体   繁体   中英

How do you get Rails has_many through relations to work with has_many conditions?

I've been working on a project that uses model associations heavily, and it seems like I've found a case where the has_many or has_one through feature conflicts with the :conditions feature. In a nutshell, the problem is that the through association creates a table alias for the intermediate table in the has_one though relationship. But I don't know how to make that table alias appear in the condition I've defined.If that's hard to follow, maybe the code will help:

class Event < ActiveRecord::Base
  has_one :event_intake, :conditions => ['event_intakes.is_draft = 1']
  has_many :product_instances, :through => :event_intake
end

class EventIntake < ActiveRecord::Base
  belongs_to :event
  has_many :product_instances, :conditions => ['is_deleted = ?', '0']
end

class ProductInstance < ActiveRecord::Base
  belongs_to :event_intake
end

Here's the MySQL query and error I'm getting:

Mysql2::Error: Unknown column 'event_intakes.is_draft' in 'on clause': 

SELECT DISTINCT `events`.id FROM `events` 
  LEFT OUTER JOIN `event_intakes` product_instances_events_join ON (`events`.`id` = `product_instances_events_join`.`event_id`) 
  LEFT OUTER JOIN `product_instances` ON (`product_instances`.`event_intake_id` = `product_instances_events_join`.`id`) AND event_intakes.is_draft = 1 
WHERE (product_instances.serial_number = '313') ORDER BY events.id DESC LIMIT 0, 50

The through association is aliasing the event_intakes table as "product_instances_events_join". But the table in the condition event_intakes.is_draft does not getting changed to match it.

I'm using rails 2.3.11, but I think the problem might apply equally to rails 3. I have read that the through association should be only used with has_many, not has_one, but I don't think this is the cause of this problem.

If I just changed the condition to "product_instances_events_join.is_draft = 1", it would fix the problem for this specific case, but break it when there is no table alias. It would be nice if I could use string interpolation on the has_one condition to get the right table name. Something like this: has_one :event_intake, :conditions => ["#{EventIntake.table_name}.is_draft = 1"] I don't think the above code would work because EventIntake's table_name doesn't change when aliasing takes place.

Another thing I've tried is redefining the has_many association on the fly before the aliasing occurs with something like this right before I make the query: Event.has_one :event_intake, :conditions => ['product_instances_events_join.is_draft = 1'] Believe it or not, this actually fixed the problem in webrick, but I'm hesitant to use this approach in a multithreaded passenger environment, because I think it's tantamount to modifying a global variable, which could potentially affect other threads. Besides, it's a hack.

Does anyone have any suggestions how to fix this? Any help would be very much appreciated.

You could put the condition on the :product_instances

class Event < ActiveRecord::Base
  has_one :event_intake 
  has_many :product_instances, :through => :event_intake,
               :conditions => ['event_intakes.is_draft = 1']
end

Having a condition on a has_one seems a bit awkward, since you really aren't filtering event_intakes but rather turning the whole association on or off. But I guess that's beside the point here.

Also, I do understand that if you have several subsequent 'through' associations that you want filtered by the condition on the intermediary, then this isn't very DRY. That's where I find myself which is how I chanced here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM