Consider these models:
class First < ActiveRecord::Base
has_many :tags
has_many :thirds, :through => :tags
end
class Second < ActiveRecord::Base
end
class Third < Second
has_many :tags
has_many :firsts, :through => :tags
end
class Tag < ActiveRecord::Base
belongs_to :first
belongs_to :third
end
In other words, we have a has_many :through 'tag-style' relationship, but with one of the models (Third) being an STI inheriting from another (Second).
Say I wanted to do a join to see all instances of Third for some value of First:
@thirds = Third.joins(:firsts).where("first.id = 2")
This will work as expected; the generated sql (via to_sql) being:
SELECT `seconds`.* FROM `seconds`
INNER JOIN `tags` ON `seconds`.`id` = `tags`.`third_id`
INNER JOIN `firsts` ON `firsts`.`id` = `tags`.`first_id`
WHERE `seconds`.`type` = 'Third' AND (first.id = 1开发者_开发问答)
This doesn't work in the other direction:
@firsts = First.joins(:thirds).where("second.id = 2")
The SQL generated being:
SELECT `firsts`.* FROM `firsts`
INNER JOIN `tags` ON `firsts`.`id` = `tags`.`first_id`
INNER JOIN `seconds` ON `seconds`.`type` = 'Third'
WHERE (second.id = 2)
This results in tag duplication due to the fact that :seconds are not joined correctly with the tag table as in the first case above (see third line of sql statement in each case). All firsts with tags will show up in the resulting table, the WHERE clause being entirely ineffectual.
If something else needs to be specified, I have not come across it. If anybody knows how to force this to work, please let me know. Otherwise, I'm assuming this is a bug in Rails. Oh, and please don't suggest I use the old model.find() methods which are due to be deprecated.
UPDATE:
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6608-generated-sql-for-has_many-through-relation-wrong-when-used-with-sti
So according to the guy who confirmed this was a bug, 'if you define the association in the base class it works as expected though.' Does anyone know what that means/how to do this?
Little changes in your queries:
@thirds = Third.joins(:firsts).where(:firsts => {:id => 2})
@firsts = First.joins(:thirds).where(:thirds => {:id => 2})
I think you should try to add to your STI model some coomon stuff:
class Third < Second
has_many :tags
has_many :firsts, :through => :tags
def self.model_name
name = "seconds"
name.instance_eval do
def plural; pluralize; end
def singular; singularize; end
def i18n_key; singularize; end
def human(*args); singularize; end
end
return name
end
end
and check this great reading http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html
or as you can read in that link you can use this method which looks little cleaner
def self.inherited(child)
child.instance_eval do
def model_name
Second.model_name
end
end
super
end
精彩评论