Включить связанную модель для всех объектов (в действии индекса)

Я пытаюсь разработать рейтинги для своего приложения, где пользователь может установить конкретный рейтинг для комментария. Я следовал следующему руководству, чтобы сделать это.

Вот мои ассоциации:

class Rating < ActiveRecord::Base
  belongs_to :comment
  belongs_to :user
end

class Comment < ActiveRecord::Base
  has_many :ratings
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :ratings
  has_many :comments
end

Моя проблема здесь в том, что в действии index моего контроллера комментариев мне нужно включить рейтинг, который пользователь сделал для этого комментария. В учебнике показано, как выбрать определенный рейтинг, выполнив следующие действия:

@rating = Rating.where(comment_id: @comment.id, user_id: @current_user.id).first 

unless @rating 
  @rating = Rating.create(comment_id: @comment.id, user_id: @current_user.id, score: 0) 
end

Однако у меня будет несколько рейтингов, потому что в моем контроллере есть:

def index
  @comments = @page.comments #Here each comment should have the associated rating for the current_user, or a newly created rating if it does not exist.
end

person Hommer Smith    schedule 29.05.2014    source источник


Ответы (1)


Вы хотите найти рейтинг комментария, где user_id рейтинга соответствует текущему пользователю.

<%= comment.ratings.where(user_id: current_user.id).first %>

Однако такая логика довольно громоздка в представлениях, лучшей стратегией было бы определить область в Rating, которая возвращает все рейтинги, сделанные конкретным пользователем.

class Rating
  scope :by_user, lambda { |user| where(user_id: user.id) }
end

class Comment
  # this will return either the rating created by the given user, or nil
  def rating_by_user(user)
    ratings.by_user(user).first 
  end
end

Теперь, на ваш взгляд, у вас есть доступ к рейтингу комментария, созданного текущим пользователем:

<% @comments.each do |comment| %>
  <%= comment.rating_by_user(current_user) %>
<% end %>

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

def index
  @comments = page.comments.includes(:ratings)
end

Затем вы можете найти правильный рейтинг с помощью следующего:

<% @comments.each do |comment| %>
  <%= comment.ratings.find { |r| r.user_id == current_user.id } %>
<% end %>

Это вернет правильную оценку без создания каких-либо дополнительных SQL-запросов за счет загрузки каждой связанной оценки для каждого комментария.


Я не знаю, как в ActiveRecord можно загрузить подмножество отношения has_many. См. этот вопрос о StackOverflow, а также это сообщение в блоге, в котором содержится дополнительная информация о нетерпеливой загрузке.

person hjing    schedule 29.05.2014
comment
Итак, я не могу сделать это с включением при выполнении: @page.comments ? - person Hommer Smith; 30.05.2014
comment
Что ты имеешь в виду? Вы пытаетесь загрузить оценки и комментарии одновременно? - person hjing; 30.05.2014
comment
hjing, в идеале page.comments, где каждый комментарий имеет рейтинг для текущего пользователя. да. - person Hommer Smith; 30.05.2014
comment
Вы имеете в виду, что хотите загрузить каждый рейтинг для каждого комментария? guides.rubyonrails.org/, или вы хотите иметь отдельная переменная экземпляра с именем @ratings, которая содержит только рейтинги, сделанные текущим пользователем для комментариев, загруженных в переменную экземпляра @comments? - person hjing; 30.05.2014
comment
Я хотел бы загрузить каждый рейтинг (сделанный текущим пользователем) для каждого комментария. Комментарий может иметь только ОДНУ оценку для каждого пользователя. - person Hommer Smith; 30.05.2014
comment
Я обновил свой ответ альтернативами, но я не знаю, как сделать то, что вы ищете. Если вы беспокоитесь о производительности, я, вероятно, постараюсь найти хорошую стратегию кэширования. - person hjing; 30.05.2014