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.