簡體   English   中英

如何在沒有其他查詢的情況下通過關聯對象的相關聯接模型訪問has_many:?

[英]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.*")

這將使您能夠訪問所有內容

如果您希望訪問的數據是聯接表上的屬性,則包括是一種非常干凈的方法。

但是,從您的帖子中看來,您似乎有一種關於成員資格的方法,該方法希望對基礎數據進行一些智能化處理。 另外,似乎您想對一個查詢做兩件事:

  1. 輸出用戶信息(這就是為什么要首先迭代用戶對象的原因,如果您只是想要成員身份,則只對那些對象進行迭代)。
  2. 為您的成員對象輸出一些智能的和已處理的信息。

正如您所注意到的,您在這里所做的任何事情都感覺很奇怪,因為無論您將代碼放在何處,都不會感覺像是正確的模型。

我通常將這種感覺識別為需要另一個抽象層。 考慮創建一個名為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM