How to preload a has_one through a has_many in Rails 5.2?

Viewed 982

I am looking for a way to preload a has_one relation when it is through a has_many. I have 4 relevant models:

class Order
  has_many :line_items
  has_many :products, through: :line_items

  has_one :review
  has_many :review_items, through: :review
end

class Review
  belongs_to :order
  has_many :review_items
  has_many :products, through: :review_items
end

class ReviewItem
  belongs_to :review
  belongs_to :product

  has_one :order, through: :review
end

class LineItem
  belongs_to :order
  belongs_to :product

  has_one :review, through: :order

  # HERE IS RELATION IN QUESTION
  has_one :review_item, ->(product) { where(product_id: line_item. product_id, review: line_item.review) }, foreign_key: "product_id", primary_key: "product_id"

end

The question is how to preload the review_item on LineItem when fetching it through a line_item so I can do something like Order.include(line_items: [:review_item]).where(user: user)

Currently, I get the error

The association scope review_item is instance dependent (the scope block takes an argument). Preloading instance dependent scopes is not supported.)

So, I'm looking for a way to have that has_one relationship, but not have to pass an instance when querying it.

The query should be simple, it is the same as doing

# in LineItem
  def review_item
    review.find_by(meal_id: self.meal_id)
  end

but obviously I cannot preload that.

I feel like there must be a way to write the has_one relation that does not take an instance of LineItem, but I am struggling to find it.

1 Answers

Your relation has to take argument because you do not have direct relation between LineItem and ReviewItem.

Actually in the problem domain the customer is reviewing not the Product in general, but the actual product instance that was ordered and delivered to them (=LineItem), so it's logical to have:

class ReviewItem
  belongs_to :review
  belongs_to :line_item
  has_one :product, through: :line_item
  ...
end
Related