【问题标题】:Wrong sql generated by ActiveRecord for has_many :through relation with STIActiveRecord 为 has_many 生成的错误 sql:通过与 STI 的关系
【发布时间】:2011-03-23 14:06:16
【问题描述】:

考虑这些模型:

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

换句话说,我们有一个 has_many :through 'tag-style' 关系,但其中一个模型(第三个)是从另一个(第二个)继承的 STI。

假设我想做一个连接来查看所有 Third 的实例以获得 First 的某些值:

@thirds = Third.joins(:firsts).where("first.id = 2")

这将按预期工作;生成的 sql(通过 to_sql)是:

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)

这在另一个方向上不起作用:

@firsts = First.joins(:thirds).where("second.id = 2")

生成的 SQL 是:

SELECT `firsts`.* FROM `firsts` 
INNER JOIN `tags` ON `firsts`.`id` = `tags`.`first_id` 
INNER JOIN `seconds` ON `seconds`.`type` = 'Third'
WHERE (second.id = 2)

这会导致标签重复,因为 :seconds 没有像上面的第一种情况那样正确地与标签表连接(参见每种情况下的第三行 sql 语句)。所有带标签的第一个都将显示在结果表中,WHERE 子句完全无效。

如果需要指定其他内容,我还没有遇到过。如果有人知道如何强制它工作,请告诉我。否则,我假设这是 Rails 中的一个错误。哦,请不要建议我使用即将被弃用的旧 model.find() 方法。

更新:

https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6608-generated-sql-for-has_many-through-relation-wrong-when-used-with-sti

因此,根据确认这是一个错误的人的说法,“如果您在基类中定义关联,它会按预期工作。” 有谁知道这意味着什么/如何做到这一点?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-3 activerecord sti


    【解决方案1】:

    查询中的小变化:

    @thirds = Third.joins(:firsts).where(:firsts => {:id => 2})
    @firsts = First.joins(:thirds).where(:thirds => {:id => 2})
    

    我认为你应该尝试在你的 STI 模型中添加一些 coomon 的东西:

    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
    

    并查看这篇精彩的阅读http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html

    或者您可以在该链接中阅读,您可以使用这种看起来不太干净的方法

    def self.inherited(child)
      child.instance_eval do
        def model_name
          Second.model_name
        end
      end
      super
    end
    

    【讨论】:

    • 谢谢,我今天回家试试这个。真的没想到这么快就回复了。上面链接的文章中是否解释了需要这样做的原因?你能解释一下为什么这会强制 activerecord:query 构造正确的语句吗?
    • 我刚试过。建议的更改都没有任何效果。我真的没想到会这样,因为问题不在于 Rails 不知道模型名称。确实如此。在 STI 的情况下,它只是没有考虑到关系的 :through 部分。不过我很感激这个建议。
    • 我昨晚报告了这个,显然,它 Rails 中的一个错误:
    猜你喜欢
    • 2011-09-28
    • 1970-01-01
    • 1970-01-01
    • 2020-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-16
    相关资源
    最近更新 更多