【问题标题】:ActiveRecord has_many with custom foreign key sql expression带有自定义外键 sql 表达式的 ActiveRecord has_many
【发布时间】:2026-02-03 16:05:01
【问题描述】:

在 ActiveRecord 中,has_many 关系使用外键列来加载关联。所以:

class Person < ActiveRecord::Base
  has_many :messages
end

class Messages < ActiveRecord::Base
  belongs_to :person
end

Person.limit(10).includes(:messages) 
# select * from persons limit 10;
# select messages.* from messages where person_id in (1, 2, 3...)

我有一个案例(我也看到其他人也要求这样做),我不希望 Rails 自动将外键检查附加到 where 子句。相反,我可能想要这样的东西:

class Person < ActiveRecord::Base
  has_many :messages, 
           :foreign_key => false, 
           :conditions => proc { ["person_id is null or person_id = ?", self.id] }
end

Person.limit(10).includes(:messages)
# select messages.* from messages where person_id is null or person_id in (1, 2, 3...)

我该怎么做?综上所述,我不希望 ActiveRecord 自动在 WHERE 子句中附加外键,我希望能够指定它用于关联的表达式。

我不想这样做:

class Person < ActiveRecord::Base
  def messages
    Message.where("person_id is null or person_id = #{ self.id }")
  end
end

据我所知,这会破坏急切加载。

我也不想对 has_many 使用 finder_sql 选项,因为这会破坏 person.messages.where(:id =&gt; 1)Person.limit(10).includes(:messages =&gt; :image) 之类的东西

【问题讨论】:

  • 可能以另一种方式来问同样的问题:是否可以在没有外键的情况下建立 has_many 关系?我可以改用 :conditions 选项。
  • 我敢打赌,有一些充分的理由说明为什么不能通过关联来做你想做的事。关系和范围可能会发生很多并发症和副作用。也许这个问题可以在 Rails 邮件列表中得到更好的回答。
  • @JoeVanDyk 我知道我在这里挖掘了一块旧化石,但你有没有找到办法做到这一点?

标签: ruby-on-rails postgresql activerecord associations


【解决方案1】:

这样的东西在 Rails 3.2.1 上对我有用:

class Person < ActiveRecord::Base
  has_many :messages, :finder_sql => lambda{ "SELECT * FROM messages WHERE messages.person_id=#{id} OR messages.person_id IS NULL" }
end

# person = Person.includes(:messages).first
# person.messages.loaded?  #=> true

【讨论】:

  • 不幸的是,finder_sql 中断了链接。所以: Person.limit(10).includes(:messages => :image) 不起作用。甚至 person.messages.where(:id => 1) 也不起作用。我曾经在门票说明中提到过,但我不小心删除了它。我会重新添加它。