モデルプロフィールがあります。プロファイルhas_oneユーザー。ユーザーモデルにはフィールドメールがあります。電話すると
Profile.some_scope.includes(:user)
呼び出す
SELECT users.* FROM users WHERE users.id IN (some ids)
しかし、私のUserモデルには、レンダリングに使用していないフィールドがたくさんあります。ユーザーからのメールのみを読み込むことはできますか?したがって、SQLは次のようになります。
SELECT users.email FROM users WHERE users.id IN (some ids)
Railsにはincludeクエリのオプションを渡す機能がありません。しかし、モデルの下で関連付け宣言を使用してこれらのパラメーターを渡すことができます。
あなたのシナリオでは、以下のように、プロファイルモデルの下にユーザーモデルとの新しい関連付けを作成する必要があります。
belongs_to :user_only_fetch_email, :select => "users.id, users.email", :class_name => "User"
関連付けをもう1つ作成しましたが、それはユーザーモデルのみを指します。したがって、クエリは次のようになります。
Profile.includes(:user_only_fetch_email)
または
Profile.includes(:user_only_fetch_email).find(some_profile_ids)
モデルに属している追加のものが必要です。
単純な関連付けの場合:
_belongs_to :user_restricted, -> { select(:id, :email) }, class_name: 'User'
_
ポリモーフィック関連(たとえば、_:commentable
_)の場合:
_belongs_to :commentable_restricted, -> { select(:id, :title) }, polymorphic: true, foreign_type: :commentable_type, foreign_key: :commentable_id
_
任意の_belongs_to
_の名前を選択できます。上記の例では、Article.featured.includes(:user_restricted)
、Comment.recent.includes(:commentable_restricted)
などのように使用できます。
Railsは、includes
の場合、特定の列を選択することをサポートしていません。ご存知、それはlazy load
だけです。
ActiveRecord :: Associations :: Preloader モジュールを使用して、データが実際に使用する前に関連データをロードします。 メソッド :
def preload(records, associations, preload_scope = nil)
records = Array.wrap(records).compact
if records.empty?
[]
else
records.uniq!
Array.wrap(associations).flat_map { |association|
preloaders_on association, records, preload_scope
}
end
end
preload_scope
preload
の3番目のパラメーターは、指定列を選択する方法です。 しかし、もう遅延読み込みはできません。
relation = Profile.where(id: [1,2,3])
user_columns = {:select=>[:updated_at, :id, :name]}
preloader = ActiveRecord::Associations::Preloader.new
preloader.preload(relation, :user, user_columns)
渡した指定列を選択します。ただし、これは単一の関連付けのためだけです。複数の複雑な関連付けを一度に読み込むことをサポートするには、ActiveRecord::Associations::Preloader
のパッチを作成する必要があります。
自分でその機能が欲しかったので使ってください。このメソッドをクラスに含める
#ACCEPTS args in string format "ASSOCIATION_NAME:COLUMN_NAME-COLUMN_NAME"
_def self.includes_with_select(*m)
association_arr = []
m.each do |part|
parts = part.split(':')
association = parts[0].to_sym
select_columns = parts[1].split('-')
association_macro = (self.reflect_on_association(association).macro)
association_arr << association.to_sym
class_name = self.reflect_on_association(association).class_name
self.send(association_macro, association, -> {select *select_columns}, class_name: "#{class_name.to_sym}")
end
self.includes(*association_arr)
end
_
そして、あなたは次のように呼び出すことができます:Contract.includes_with_select('user:id-name-status', 'confirmation:confirmed-id')
そしてそれはそれらの指定された列を選択します。
Mohanajの例を使用すると、これを行うことができます。
belongs_to :user_only_fetch_email, -> { select [:id, :email] }, :class_name => "User"