开发者

Wrong sql generated by ActiveRecord for has_many :through relation with STI

开发者 https://www.devze.com 2023-02-19 19:14 出处:网络
Consider these models: class First < ActiveRecord::Base has_many :tags has_many :thirds, :through => :tags

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
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号