我有模特个人资料。个人资料has_one用户。用户模型有现场电子邮件。当我打电话
Profile.some_scope.includes(:user)
它调用
SELECT users.* FROM users WHERE users.id IN (some ids)
但是我的用户模型有许多我在渲染时没有使用的字段。是否可以仅加载用户的电子邮件?所以,SQL应该看起来像
SELECT users.email FROM users WHERE users.id IN (some ids)
答案 0 :(得分:33)
Rails无法通过包含查询的选项。但我们可以通过模型下的关联声明传递这些参数。
对于您的场景,您需要在配置文件模型下创建与用户模型的新关联,如下所示
belongs_to :user_only_fetch_email, :select => "users.id, users.email", :class_name => "User"
我刚创建了一个关联,但它只指向User模型。所以你查询将是,
Profile.includes(:user_only_fetch_email)
或
Profile.includes(:user_only_fetch_email).find(some_profile_ids)
答案 1 :(得分:11)
如果您想选择特定属性,则应使用joins
而不是includes
。
从此asciicast:
include选项实际上不能与select选项一起使用,因为我们无法控制SELECT语句的第一部分是如何生成的。如果您需要控制SELECT中的字段,那么您应该使用include而不是连接。
使用joins
:
Profile.some_scope.joins(:users).select("users.email")
答案 2 :(得分:7)
你需要额外的属于模型。
简单关联:
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)
等。
答案 3 :(得分:2)
我自己想要这个功能,所以请使用它。 在您的课程中包含此方法
#ACCEPTS args采用字符串格式" 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')
,它会选择指定的列。
答案 4 :(得分:2)
includes
时,Rails不支持选择特定的列。您知道,它只是lazy load
。
它使用ActiveRecord::Associations::Preloader模块在实际使用数据之前加载关联的数据。通过method:
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
的第三个参数是选择指定列的一种方法。 但是不能再懒加载。
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
创建一个补丁,以支持一次加载多个复杂的关联。
答案 5 :(得分:0)
使用Mohanaj的例子,你可以这样做:
belongs_to :user_only_fetch_email, -> { select [:id, :email] }, :class_name => "User"