#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. Однако, поскольку вы извлекаете его по странице, он будет извлекать только отзывы, которые принадлежат этим элементам.