ActiveRecord唯一性验证可防止更新

时间:2015-03-27 17:39:35

标签: ruby-on-rails validation activerecord

我正在使用Rails编写Web应用程序,其中一部分包括让用户能够为事物留下评论。我想在review模型中进行验证,以确保一个用户不能对同一项目进行多次审核,因此我写了这个:

class NoDuplicateReviewValidator < ActiveModel::Validator
  def validate(record)
    dup_reviews = Review.where({user_id: record.user,
                                work_id: record.work})
    unless dup_reviews.length < 1
      record.errors[:duplicate] << "No duplicate reviews!"
    end
  end
end

此验证器具有所需的行为,即它保证用户无法两次查看作品。然而,它具有不期望的副作用,即用户无法更新他/她离开的现有评论。我使用的是一个非常简单的

def update
  @review.update(review_params)
  respond_with(@work)
end

在评论控制器中。如何更改验证程序或更新方法以防止重复审核但允许更新?

我对Rails和网站开发都很陌生,所以我确定我在这里做了一些傻瓜。我没有使用其中一个内置unique验证器,因为唯一的是user / work对;不同作品的同一用户可以进行多次审核,不同用户可以对同一作品进行多次审核。

2 个答案:

答案 0 :(得分:2)

您可以对多个属性使用validates_uniqueness_of,如下所示:

validates_uniqueness_of :user_id, :scope => :work_id

然后,不允许用户查看已经审核过的作品。

答案 1 :(得分:0)

@Sharvy Ahmed的回答绝对是最好的,只要情况足够简单– OP的情况似乎就是其中之一。

但是,如果条件更复杂,则可能需要/希望编写自定义验证。为此,这里有一个示例(已在Rails 6.0中选中)。

class NoDuplicateReviewValidator < ActiveModel::Validator
  def validate(record)
    dup_reviews = Review.where(user_id: record.user,
                               work_id: record.work)
    dup_reviews = dup_reviews.where.not(id: record.id) unless record.new_record?

    if dup_reviews.count > 0
      record.errors[:duplicate] << "No duplicate reviews!"
    end
  end
end

这个想法是

  1. 在创建过程中,可以并且应该使用用where检索的所有相关数据库记录来判断唯一性。在示例中,new_record?用于检出,但实际上是多余的(因为nil id与记录不匹配)。
  2. 在更新中,必须从唯一比较中排除要更新的record的DB行。否则,更新将始终无法通过验证。
  3. 在数据库事务方面,count方法效率更高。