Topic: Complicated has_many/has_one through association problem

I've been working on a project that uses model associations heavily, and it seems like I've found a case where the has_one through feature conflicts with the has_one :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

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

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 DESC LIMIT 0, 50

As you can see, 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. (the query succeeds if I make this modification)

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.

Last edited by jsarma (2012-08-04 07:47:20)