2016-08-23 8 views
0

これはかなり基本的な質問ですが、何らかの理由で私はそれを(Rails newbie) (私は適切に検索していないかもしれない)。has_many:throughアソシエーションを反復処理するときに関連付けられた結合モデルにアクセスする

だから私は基本的にhas_manyを持っています。このような関係を通じて:

<% @user.contacts.each do |c| %> 
    <%= c.name %> 
<% end %> 

:ユーザー/ show.html.erbで

class User < ApplicationRecord 
    has_many :contacts, through :user_contacts 

class Contact < ApplicationRecord 
    has_many :users, through :user_contacts 

私は次のように、単一のユーザーの連絡先を反復処理していますそのループの中で、私は与えられたユーザーと連絡先に関連付けられたuser_contact結合モデルにアクセスして、ユーザー< - >連絡関係が作成されたことを示すcreated_atタイムスタンプを表示したいと考えています。

私はuser_actとcontact_idでデータベース内のモデルを検索するためにUserContact.find呼び出しを行うことができますが、何とかこれは余分な感じです。これがどういう仕組みかを正しく理解していれば、私はそうでない可能性があります。与えられたユーザーとその連絡先を既にデータベースからロードしたときに、user_contactモデルが既に読み込まれているはずです。私はちょうど正しいモデルに正しくアクセスする方法を知らない。誰かが正しい構文を助けることができますか?

答えて

1

実際にインクルードは、モデルへの参加はまだロードされていません:ActiveRecordのは正しいContactレコードを照会するためJOINステートメントをそのSQLを構築するためにthrough仕様をかかりますが、効果のみをインスタンス化します。

@user.user_contacts.includes(:contact).find_each do |uc| 
    # now you can access both join model and contact without additional queries to the DB 
end 

あなたがuc.contact.somethingでコードを乱雑にせずに読めるものを維持したい場合は、そのデリゲートUserContactモデル内の委任を設定することができます:あなたはこのようにSTHを行うことができ、あなたがUserContactモデルを持っていると仮定すると

いくつかのプロパティはそれぞれcontactまたはuserになります。たとえば、この

class UserContact < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :contact 
    delegate :name, to: :contact, prefix: true 
end 

はあなたがすべての

uc.contact_name 
1

最初に書くことができるようになる、has_many :things, through: :other_things句は:thingsを見つけるためにother_things関係を探すために起こっています。

SQLクエリでパフォーマンスを向上させるために、魔法を組み込んだソートのメソッド呼び出しと考えてください。

def contacts 
    user_contacts.map { |user_contact| user_contact.contacts }.flatten 
end 

user_contactsのコンテキストが完全に失われる:だからthrough句を使用することによって、あなたは、多かれ少なかれのようなものをやっています。

user_contactsのように見えるので、1対1の結合です。あなたはそれがあなたのコントローラ内でこのような何かを行うことができますN + 1クエリせずに、それらのレコードをロードすることを言及する価値がレールに新しいしているので、また

<% @user.user_contacts.each do |user_contact| %> 
    <%= user_contact.contact.name %> 
<% end %> 

:このような何かをするために容易になるだろう

このように
@user = User.includes(user_contacts: [:contacts]).find(params[:id]) 
+0

ありがとうございました。将来的にこの問題を抱えている人のための1つの説明として、インクルードの正しい構文は.findの前です:@user = User.includes(user_contacts:[:contact])。find(params [:id]) – Katie

+0

Ahh、あなたが正しい。 'find'は' ActiveRecord :: Relation'を返しません。ありがとう、私はそれを修正します。 – Azolo

0

使用.joins.select:内側今

@contacts = current_user.contacts.joins(user_contacts: :users).select('contacts.*, user_contacts.user_contact_attribute_name as user_contact_attribute_name')

、ループでは、contact.user_contact_attribute_nameに電話することができます。 contactuser_contact_attribute_name、唯一UserContactがないことはありませんが、クエリの.select部分は各contactインスタンス上であなたにそれが魔法利用できるようになりますので、それは奇妙に見える

contacts.*部分は、contactのすべての属性を利用できるようにするためのクエリです。

関連する問題