渴望从Rails 5中的实例化ActiveRecord对象加载单一关联的关联

时间:2018-04-05 16:43:48

标签: ruby-on-rails rails-activerecord

我有一个模型Userhas_one :childChild模型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查询。

2 个答案:

答案 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])