Как я могу выполнять пакетные запросы к ассоциациям в Rails?

#ruby-on-rails #activerecord

#ruby-on-rails #activerecord

Вопрос:

Допустим, у меня есть модель продукта, модель пользователя и модель отзывов, определенная следующим образом:

 class Product
  has_many :reviews
end

class User
  has_many :reviews
end

class Review
  belongs_to :product
  belongs_to :model
end
  

Теперь в методе контроллера у меня есть current_user переменная. И я пытаюсь получить список продуктов вместе с их обзорами от current_user .

В настоящее время я делаю что-то вроде этого:

 class ProductsController
  def index
    @products = Product.page(1)
  end
end

# in index.html.erb

<%= @products.each do |product| %>
  <%= product.reviews.find_by(user: current_user).rating %>
<% end %>
  

Но он генерирует один SQL-запрос для каждого product.reviews.find_by вызова. Могу ли я в любом случае избежать этого?

Я попытался изменить код в контроллере, чтобы:

 class ProductsController
  def index
    @products = Product.page(1).include(:reviews)
  end
end
  

который не работает. Я использую это неправильно?

Заранее спасибо.

Ответ №1:

Используйте это следующим образом

 # controller
@products = Product.includes(:reviews).page(1)

# view
<%= @products.each do |product| %>
  <%= product.reviews.select{ |user| user.id == current_user.id }.rating %>
<% end %>
  

http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

Редактировать: Вы также можете фильтровать по включениям подобным образом для повышения производительности

 @products = Product.includes(:reviews).where("reviews.user_id" => 1).page(1)
  

Комментарии:

1. Работает как шарм! Здесь следует использовать просто nit: detect вместо select . На самом деле, согласно моему образцу кода, это должно быть: product.reviews.detect { |review| review.user_id == current_user.id }

2. Извините, но позвольте мне задать другой вопрос, вызовет ли это какие-либо проблемы с производительностью, если продукт имеет действительно большое количество отзывов?

3. Однако, поскольку вы извлекаете его по странице, он будет извлекать только отзывы, которые принадлежат этим элементам.