33

我有一个问题类:

class Question < ActiveRecord::Base
  attr_accessible :user_id, :created_on

  validates_uniqueness_of :created_on, :scope => :user_id
end

给定的用户每天只能创建一个问题,因此我想通过唯一索引强制数据库中的唯一性,并通过 validates_uniqueness_of 强制问题类。

我遇到的麻烦是我只希望非管理员用户使用该约束。因此,管理员每天可以根据需要创建任意数量的问题。关于如何优雅地实现这一目标的任何想法?

4

3 回答 3

89

您可以通过将要执行的简单 Ruby 字符串、Proc 或方法名称作为符号作为值传递给验证选项:if:unless在选项中传递验证条件。这里有些例子:

在 Rails 5.2 版本之前,您可以传递一个字符串:

# using a string:
validates :name, uniqueness: true, if: 'name.present?'

从 5.2 开始,不再支持字符串,为您提供以下选项:

# using a Proc:
validates :email, presence: true, if: Proc.new { |user| user.approved? }

# using a Lambda (a type of proc ... and a good replacement for deprecated strings):
validates :email, presence: true, if: -> { name.present? }

# using a symbol to call a method:
validates :address, presence: true, if: :some_complex_condition

def some_complex_condition
  true # do your checking and return true or false
end

在您的情况下,您可以执行以下操作:

class Question < ActiveRecord::Base
  attr_accessible :user_id, :created_on

  validates_uniqueness_of :created_on, :scope => :user_id, unless: Proc.new { |question| question.user.is_admin? }
end

请查看 rails 指南上的条件验证部分以获取更多详细信息:http ://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation

于 2014-01-04T00:20:33.483 回答
5

我知道保证唯一性的唯一方法是通过数据库(例如唯一索引)。所有基于 Rails 的方法都涉及竞争条件。鉴于您的限制,我认为最简单的方法是建立一个单独的、唯一索引的列,其中包含您留给null管理员的日期和用户 ID 的组合。

至于,您可以通过使用or选项validates_uniqueness_of将验证限制为非管理员,如http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of中所述ifunless

于 2014-01-04T00:07:21.337 回答
2

只需在validates_uniqueness_of调用中添加条件即可。

validates_uniqueness_of :created_on, scope: :user_id, unless: :has_posted?
def has_posted
  exists.where(user_id: user_id).where("created_at >= ?", Time.zone.now.beginning_of_day)
end

但更好的是,只需创建一个自定义验证:

validate :has_not_posted
def has_not_posted
  posted = exists.where(user: user).where("DATE(created_at) = DATE(?)", Time.now)
  errors.add(:base, "Error message") if posted
end
于 2014-01-04T00:20:39.623 回答