渴望使用实例变量加载自定义联接

Posted

技术标签:

【中文标题】渴望使用实例变量加载自定义联接【英文标题】:Eager loading a custom join with an instance variable 【发布时间】:2015-08-16 18:42:00 【问题描述】:

给定一个允许用户邀请其他用户参加活动的系统:

class Event
  has_many :invites
end

class User
  has_many :invites
  has_many :invited, inverse_of: :inviter, foreign_key: :inviter_id, class_name: 'Invite'
end

class Invite
  belongs_to :user
  belongs_to :event
  belongs_to :inviter, class_name: 'User'
  has_many :invited, -> (invite)  where(invites:  event_id: invite.event_id ) , through: :user, class_name: 'Invite'
end

我正在生成一个简单的报告,其中显示了某个活动的所有邀请。我正在尝试修改报告以包含一些可以针对示例事件的invited 执行的操作:

<%- event.includes(:user).each do |invite| -%>
  <%= invite.user.name %>
  <%- invite.invited.each do |other| -%>
    <%= link_to invite_path(other), method: :destroy do %>
      ...
    <% end %>
  <%- end -%>
<%- end -%>

这工作正常,但有一个 N+1 问题。尝试预先加载自定义关联会给出:

弃用警告:关联范围“已邀请”取决于实例(范围块接受参数)。预加载发生在创建单个实例之前。这意味着没有实例被传递到关联范围。这很可能会导致损坏或不正确的行为。这些关联的加入、预加载和预加载已被弃用,将来将被删除。

有没有办法像这样进行急切加载?是否可以给has_many 提供其他提示而不是使用实例变量?

【问题讨论】:

我正在尝试了解您要获取的内容。来自同一用户的邀请参加同一活动? @D-Side 是的,试图获得每个受邀用户的其他邀请。实际的例子要复杂得多,但我尽量简化了这个问题。 嗯,您正在获取每个邀请的邀请集合。这将是一个痛苦,因为它需要一个自连接,而这又需要对所涉及的表实例之一进行别名。所以,Arel 的使用似乎是必须的。 您是否需要按... 嗯... 邀请对邀请进行分组?我的意思是,一个奇怪的排序列表是否足以满足您的需求,还是您需要按相关邀请整齐地分组? 【参考方案1】:

见Rails 5 how to form association between tables on multiple shared attributes。

您可能需要定义两个不同的关联。一个特定于实例,另一个在连接期间使用。

【讨论】:

【参考方案2】:

尝试从事件端使用关系。喜欢:

class Invite
  belongs_to :user
  belongs_to :event
  belongs_to :inviter, class_name: 'User'

  def invited
    event.invites.includes(...)
  end
end

【讨论】:

基于 OP Invite.invited 查询其他 invites 与邀请具有相同的 inviter(user) 和 event,所以我认为您的查询 event.invites 不够。你能解释更多细节吗?

以上是关于渴望使用实例变量加载自定义联接的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Rails 上的自定义布局访问视图上的实例变量

NSManagedObject - 跨线程设置自定义实例变量

使用内部对象的实例变量的值对自定义对象数组进行排序

如何将自定义数组保存/重新加载到 plist

黑马程序员——————> 自定义序列化

静态变量与实例变量的区别