这是我现在已经遇到过很多次的事情,我很想知道如何做我想要的事情或构建并向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提醒我这个)。
答案 0 :(得分:2)
如果你只是想访问会员资格中的一些属性,那就有一个丑陋的把戏
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
答案 1 :(得分:0)
如果您要访问的数据是连接表上的属性,那么包含这是一种非常干净的方法。
但是,从您的帖子看,您似乎有一个成员资格的方法,希望对基础数据进行一些智能工作。此外,您似乎想要使用一个查询执行两项操作:
正如你所注意到的,你在这里做的任何事情都会让人觉得奇怪,因为无论你把代码放在哪里,它都感觉不像是正确的模型。
我通常认为这种感觉需要另一个抽象层。考虑创建一个名为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
通过创建一个表示用户及其成员资格的类到指定的组,您已经创建了一个foo方法感觉合适的上下文。我猜测foo在没有特定用户的上下文中并没有多大意义,并且你在foo方法中引用了相关的用户。
-Nick(@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
答案 2 :(得分:0)
你可以这样做:
获取特定群组成员资格中的所有用户:
其中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
答案 3 :(得分:0)
users = Group.first.users.select("*, memberships.data as memberships_data, users.*")
这将使您可以访问所有内容