[英]How do I access attributes of the join model in a has_many :through association
[英]How can I access a has_many :through association object's relevant join model without additional queries?
这是我现在遇到过很多次了,我很想弄清楚如何做自己想要的事情,或者构建并提交一个补丁来向Rails提交。 在我的应用程序中,很多时候我会有一些看起来像这样的模型:
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, through: :memberships
end
class Membership
belongs_to :user
belongs_to :group
def foo
# something that I want to know
end
end
class Group
has_many :memberships
has_many :users, through: :memberships
end
我想要做的是从调用关联中访问相关成员资格,而无需执行其他查询。 例如,我想做这样的事情:
@group = Group.first
@group.users.each do |user|
membership = user.membership # this would be the membership for user in @group
end
Rails中有什么允许这样做的吗? 因为我知道要达到我所谈论的结果的唯一方法非常丑陋且效率低下,所以这样的事情:
@group.users.each do |user|
membership = Membership.where(group_id: @group.id, user_id:user.id).first
end
ActiveRecord是否具有一些不可思议的内置工具来实现这一目标? 似乎并不难,它已经必须获取连接模型以正确地检索关联,因此,如果不存在此功能,我认为应该这样做。 我已经遇到过很多次了,并且准备好收起袖子并永久解决它。 我能做什么?
更新:我可以使用的另一种模式基本上可以得到我想要的东西,像这样:
@group.memberships.includes(:user).each do |membership|
user = membership.user
end
但是从美学上讲,我不喜欢这种解决方案,因为我实际上对成员身份并不感兴趣,因为我是用户,并且对联接模型(而不是关联目标)进行迭代是错误的。 但这比我上面指出的另一种方法要好(感谢Intridea的Ian Yang提醒我这一点)。
如果您只想访问成员资格中的某些属性,则有一个丑陋的窍门
group.users.select('*, memberships.some_attr as membership_some_attr')
之所以有效,是因为成员资格隐式包含在JOIN中。
更新资料
更重要的是,如果您在User
添加了另一个丑陋的把戏
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, through: :memberships
belongs_to :membership #trick, cheat that we have membership_id
end
现在:
group.users.select('*, memeberships.id as membership_id').includes(:membership).each do |user|
membership = user.membership
end
您可以这样操作:
获取特定组成员资格内的所有用户:
其中group_array是您希望@user成为成员的组的ID数组。
@user = User.all(
include: :groups, conditions: ["memberships.group_id in (?)", group_array]
).first
用以下方法将其反转:
@group = Group.all(
include: :users, conditions: ["memberships.user_id in (?)", user_array]
).first
users = Group.first.users.select("*, memberships.data as memberships_data, users.*")
这将使您能够访问所有内容
如果您希望访问的数据是联接表上的属性,则包括是一种非常干净的方法。
但是,从您的帖子中看来,您似乎有一种关于成员资格的方法,该方法希望对基础数据进行一些智能化处理。 另外,似乎您想对一个查询做两件事:
正如您所注意到的,您在这里所做的任何事情都感觉很奇怪,因为无论您将代码放在何处,都不会感觉像是正确的模型。
我通常将这种感觉识别为需要另一个抽象层。 考虑创建一个名为MembershipUsers的新模型(这是一个糟糕的名字,但是您可以想到另一个模型)。
以下是我的临时编码尝试,未经测试,但可以使您了解解决方案:
class MembershipUser < User
def self.for_group(group)
select('memberships.*, users.*').
joins('join memberships on memberships.user_id = users.id').
where('memberships.group_id = ?', group.id)
end
def foo
# now you have access to the user attributes and membership attributes
# and you are free to use both sets of data for your processing
end
end
通过创建一个表示User及其指定组成员身份的类,您创建了一个foo方法合适的上下文。 我猜想,如果没有特定用户的上下文,foo就没有多大意义,并且您在foo方法中引用了关联的用户。
-尼克(@ngauthier)
编辑:忘记带这个完整的圆圈:
class Group
def membership_users
MembershipUser.for_group(self)
end
end
# then iterate
group.membership_users.each do |membership_user|
membership_user.user_name # a pretend method on user model
membership_user.foo # the foo method that's only on MembershipUser
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.