I see that you've already answered your question, but if you were to use :include =>
instead of :join =>
in the original query, it would generate a LEFT OUTER JOIN
. That said, you may not need to eager load the association, only query on it, so your mileage may vary.
ActiveRecord named scope on two relationships to one model
-
05-12-2021 - |
Frage
I am using a polymorphic association of two different models to a tagging model. Great, easy. But, one of these two models also belongs to the other model.
class Facility < ActiveRecord::Base
has_many :units
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
class Unit < ActiveRecord::Base
belongs_to :facility
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :taggable, :polymorphic => true
belongs_to :tag
end
class Tag < ActiveRecord::Base
has_many :taggings
end
I am trying to write a scope to retrieve all of the Units for a certain tag, including those Units belonging to a Facility with that tag. This does not work:
named_scope :by_tag_id, lambda {|tag_id|
{
:select => "DISTINCT units.*",
:joins => [:taggings, {:facility => :taggings}],
:conditions => ["taggings.tag_id = ? OR taggings_facilities.tag_id = ?",
tag_id.to_i, tag_id.to_i]
}}
For that named_scope, I only get those units that have the tag.
Any thoughts? Am I missing something obvious here?
Update
I am, unfortunately, on Rails 2.3.x, Ruby 1.8.7.
I am trying right now some techniques with more explicitly written joins.
Update 2
I was not able to answer my own question because I have no reputation. Oops. I should really contribute more...
Well, I guess I just needed to poke and push a bit more. Here is what I have working:
named_scope :by_tag_id, lambda {|tag_id|
{
:select => "DISTINCT units.*",
:joins => [
"INNER JOIN facilities as bti_facilities ON units.facility_id = bti_facilities.id",
"LEFT JOIN taggings AS bti_facilities_taggings
ON bti_facilities_taggings.taggable_id = bti_facilities.id AND bti_facilities_taggings.taggable_type = 'Facility'",
"LEFT JOIN taggings AS bti_units_taggings ON bti_units_taggings.taggable_id = units.id AND bti_units_taggings.taggable_type = 'Unit'",
],
:conditions => ["bti_units_taggings.tag_id = ? OR bti_facilities_taggings.tag_id = ?", tag_id.to_i, tag_id.to_i]
}
}
I am using some intentionally obfuscated table aliases in order to try to avoid collision with other named scopes. (I did not have facilities aliased to start and I ran into a SQL error on the query with I tried to chain with another scope that referenced the facilities table.)
If anyone has a better answer, or some feedback on my method, I would love to hear it.
Lösung
Andere Tipps
I wonder if you could write it something like this?
named_scope :by_tag_id, lambda {|tag_id|
{
:select => "DISTINCT units.*",
:include => [:tags, {:facility => :taggings}],
:conditions => { :tags => { :tag_id => tag_id } }
}
}