Желание загрузить ассоциацию сингулярной ассоциации из объекта ActiveRecord, созданного в Rails 5

У меня есть модель User, у которой has_one :child, а Child model has_one :toy.

Если у меня есть один экземпляр пользовательского класса 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

Декларации модельной модели:

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-запрос.

Ответ 1

Если вы хотите, чтобы загрузить ассоциацию сингулярной ассоциации для уже созданной записи, вы можете сделать это:

user.association(:child).target = user.association(:child).scope.eager_load(:toy).first

Это похоже на один из подходов, перечисленных в верхней части вашего вопроса. В частности, обратите внимание на часть .target.

ActiveRecord не оптимизирован для этого конкретного сценария, поэтому код довольно уродлив. По этой причине я бы сильно :child_with_toy к вашему подходу :child_with_toy если вам действительно нужно сохранить запрос.