我有一个模型User
,has_one :child
和Child
模型has_one :toy
。
如果我有一个User类user
的单个实例,如何在一个查询中加载子项和玩具?
这里有什么不起作用:
user.child.toy # 2 queries
user.includes(child: :toy) # can't call includes on a single record
user.child.includes(:toy) # same as above
user.association(:child).scope.eager_load(:toy).first # makes the appropriate query with both child and toy... but doesn't set the object on the user model.
user.child = user.association(:child).scope.eager_load(:toy).first # makes the appropriate query, but also calls another query before setting the child :(
有没有办法做到这一点,并不涉及重新查询用户模型。即。我想避免这个
User.where(id: user.id).eager_load(child: :toy).first
Relavant模型声明:
class User < ActiveRecord::Base
has_one :child
has_one :toy, through: :child
end
class Child < ActiveRecord::Base
has_one :toy
belongs_to :user
end
class Toy < ActiveRecord::Base
belongs_to :child
end
这很有效,但并不理想。我不认为我应该仅仅因为这个原因宣布另一种关系。
class User < ActiveRecord::Base
has_one :child
has_one :toy, through: :child
has_one :child_with_toy, ->{ eager_loads(:toy) }, class_name: "Child", foreign_key: :parent_id
end
允许我调用user.child_with_toy
来获取Child
对象,user.child_with_toy.toy
来获取Toy
对象,同时只触发一个SQL查询。
答案 0 :(得分:9)
如果您想为已经实例化的记录急切加载单一关联的关联,您可以这样做:
user.association(:child).target = user.association(:child).scope.eager_load(:toy).first
这类似于您在问题顶部列出的方法之一。特别要注意.target
部分。
ActiveRecord
并未针对此特定方案进行优化,因此代码非常难看。因此,如果您确实需要保存查询,我会强烈倾向于:child_with_toy
方法。
答案 1 :(得分:1)
使用Preloader
可以更轻松地完成此操作:
ActiveRecord::Associations::Preloader.new.preload(user, child: [:toy])