web-dev-qa-db-ja.com

ActiveRecordに含まれます。含まれる列を指定する

モデルプロフィールがあります。プロファイル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)
33
ibylich

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)
35
Mohanraj

特定の属性を選択する場合は、joinsではなく includes を使用する必要があります。

これから asciicast

sELECTステートメントの最初の部分がどのように生成されるかを制御できないため、includeオプションは実際にはselectオプションと連携しません。 SELECTのフィールドを制御する必要がある場合は、インクルードではなく結合を使用する必要があります。

joinsの使用:

Profile.some_scope.joins(:users).select("users.email")
11
Chris Salzberg

モデルに属している追加のものが必要です。

単純な関連付けの場合:

_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)などのように使用できます。

8
Musaffa

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_scopepreloadの3番目のパラメーターは、指定列を選択する方法です。 しかし、もう遅延読み込みはできません

Rails 5.1.6

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のパッチを作成する必要があります。

これはパッチの例です

使用方法、例

3
EarlyZhao

自分でその機能が欲しかったので使ってください。このメソッドをクラスに含める

#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')そしてそれはそれらの指定された列を選択します。

2
ClassyPimp

Mohanajの例を使用すると、これを行うことができます。

belongs_to :user_only_fetch_email, -> { select [:id, :email] }, :class_name => "User"
0
Yan Zhao