【问题标题】:Only allow users to create one review per movie只允许用户为每部电影创建一条评论
【发布时间】:2015-09-26 13:54:03
【问题描述】:

我有我的应用设置,用户可以在其中为电影撰写评论。我想做的是限制用户每部电影只能创建一个评论。我已经设法在我的评论控制器中完成了这一点:

class ReviewsController < ApplicationController
  before_action :has_reviewed, only [:new]
  ....
  def has_reviewed?
    if Review.where(user_id: current_user.id, movie_id: @movie.id).any?
      redirect_to movie_reviews_path

      flash[:notice] = "You've already written a review for this movie."
    end
  end
end

我现在遇到的麻烦是使用 Devise 和 CanCanCan 的辅助方法将相同的逻辑转换到我的索引视图模板中。

<% if user_signed_in? && ... %> # current_user has already created a review for this movie
  <%= link_to "Edit Review", edit_movie_review_path(@movie, review) %>
<% else %>
  <%= link_to "Write a Review", new_movie_review_path %>
<% end %>

另外:有什么方法可以改进我的 has_reviewed? 方法中的查找?我觉得有更好的方法来编写它,但无法确定最合适的修复方法。

【问题讨论】:

    标签: ruby-on-rails activerecord devise rails-activerecord


    【解决方案1】:

    Why not use a validation:

    #app/models/review.rb
    class Review < ActiveRecord::Base
        validates :movie_id, uniqueness: { scope: :user_id, message: "You've reviewed this movie!" }
    end
    

    这是考虑您的review 模型belongs_to :movie


    您也可以使用 ActiveRecord 回调:

    #app/models/review.rb
    class Review < ActiveRecord::Base
       before_create :has_review?
       belongs_to :user, inverse_of: :reviews
       belongs_to :movie
    
       def has_review?
          return if Review.exists?(user: user, movie_id: movie_id)
       end
    end
    
    #app/models/user.rb
    class User < ActiveRecord::Base
       has_many :reviews, inverse_of: :user
    end
    

    有什么方法可以改进我的 has_reviewed 中的查找?方法?

      def has_reviewed?
          redirect_to album_reviews_path, notice: "You've already written a review for this album." if current_user.reviews.exists?(movie: @movie)
      end
    

    【讨论】:

      【解决方案2】:

      为什么不做一个has_reviewed?用户类的方法?

      例如

      def has_reviewed?(reviewable)
         # query in here
      end
      

      那么你应该可以在你的控制器和视图中使用它。

      【讨论】:

      • 我正在考虑这样做,但迄今为止对我来说最大的挑战是如何从模型中获取current_user
      • 这是用户类上的一个方法,所以如果你把它称为 current_user.has_review?(reviewable) 就会有当前用户?(reviewable)
      【解决方案3】:

      您将希望对 new 和 create 执行此操作。否则,精明的用户将能够发布可以通过您的新操作的帖子。

      我会将 link_to 放在 helper 或 Presenter 对象中。它通常看起来像这样。

      def create_or_edit_review_path(movie, current_user)
        return '' if current_user.blank?
      
        if current_user.review.present?
          #Generate review edit link
        else
          #generate new link
        end 
      end
      

      在那之后,在你所有的观点中,它只是

      <%= create_or_edit_review_path(@movie, current_user) %>
      

      然后在您的控制器中为 new 和 create 您可以执行之前的操作或仅在每个操作上进行重定向。

      before_action :enforce_single_review, only: [:create, :new]
      
      def enforce_single_review
        if current_user.review.present?
          redirect_to review_path(current_user.review)
        end
      end
      

      【讨论】:

      • 对于那个辅助方法,您需要在开头使用 def 这个词,对吗?
      • 我还应该注意到用户has_many 评论。所以基本上对于我得到的那个辅助方法的第三行,undefined method 'review' for #&lt;User:0x007fd631e209c8&gt; 不是has_one 关联。
      • 所以稍后将不得不调整它。相同的概念,只是检查用户是否对传递给 helper 的电影进行了电影评论。
      【解决方案4】:

      这是我的想法:

      我使用Review 模型上的find_by 方法创建了一个实例方法来检索用户的电影评论:

      class User < ActiveRecord::Base
        ....
        def movie_review(album)
          Review.find_by(user_id: self, album_id: album)
        end
      end
      

      这个方法在设置我的回调时也很方便:

      class ReviewsController < ApplicationController
        before_action :limit_review, only: [:new, :create]
        ....
        private
          def limit_review
            user_review = current_user.movie_review(@movie)
      
            if user_review.present?
              redirect_to edit_movie_review_path(@movie, user_review)
            end 
          end
      end
      

      创建了一个辅助方法来显示适当的链接以编辑创建评论。非常感谢 Austio 和他的suggestion

      module ReviewsHelper
        def create_or_edit_review_path(movie)
          user_review = current_user.movie_review(movie) if user_signed_in?
      
          if user_signed_in? && user_review.present?
            link_to "Edit review", edit_movie_review_path(movie, user_review)
          else
            link_to "Write a review", new_movie_review_path
          end
        end
      end
      

      最后这就是我在视图模板中调用助手的方式:

      ....
      <%= create_or_edit_review_path(@album) %>
      

      【讨论】:

      • 嗨,我有一个类似的例子,但我有点困惑。你有@album 还是@movie?
      猜你喜欢
      • 1970-01-01
      • 2021-05-16
      • 1970-01-01
      • 2016-08-03
      • 2015-07-05
      • 2021-10-23
      • 2013-06-02
      • 2023-03-29
      • 2011-10-15
      相关资源
      最近更新 更多