我有三种模式:
class User < ActiveRecord::Base
has_many :destinations, :through => :trips
has_many :destination_reviews
class Destination < ActiveRecord::Base
has_many :users, :through => :trips
has_many :destination_reviews
class DestinationReview < ActiveRecord::Base
belongs_to :user
belongs_to :destination
不同的用户可以为同一目的地留下评论。
我们正在构建一个表格,显示属于用户的所有目的地。用户已经为某些(但不是全部)目的地留下了评论。
控制器:
@user = User.find_by_id(params[:id])
@user_reviews = @user.destination_reviews
查看:
<% @user.destinations.each do |destination| %>
<% review = @user_reviews.find_by_destination_id(dest.id) %>
<% if review %>
<div class="review>
...show the review
</div>
<% else %>
<div class="add-review>
...prompt user to add a review
</div>
<% end %>
<% end %>
N + 1发生在@user_reviews.find_by_destination_id(dest.id)
,其中@user_reviews是用户评论的预加载列表。
如何热切加载属于该用户的所有目的地,包括用户为这些目的地留下的任何评论?
我正在寻找一些类似的查询:
dest_ids = current_user.destinations.pluck(:id)`
Destination.includes(:destination_reviews).where(id: dest_ids).where('destination_reviews.user_id = ?', user_id)
但是这会引发错误:
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "destination_reviews"
或者,如果有一种方法可以在不重新查询数据库的情况下编写@user_reviews.find_by_destination_id(dest.id)
,那就行了。
如果清楚或我是否应该添加更多详细信息,请告诉我
答案 0 :(得分:1)
最简单的方法是使用group_by
:
@user_reviews = @user.desintation_reviews.group_by(&:destination_id)
然后在视图中
<% review = @user_reviews[dest.id] %>
它为您提供一个查询以获取用户的目的地,另一个用于获取他们的评论。
或者你可以将它归结为一个带有一些连接的查询,并使用select
在Destination
上创建一些伪属性,然后你可以从{{1 }}。但是它会变得更复杂,特别是因为你正在通过一个连接表。这将是这样的(未经测试):
dest
然后这个:
current_user.destinations.
joins("LEFT OUTER JOIN destination_reviews r " +
"ON r.user_id = trips.user_id " +
"AND r.destination_id = destinations.id").
select("destinations.*, r.foo AS review_foo, r.bar AS review_bar")