Ruby-on-rails Rails 仅在有条件的情况下验证唯一性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20914899/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rails validate uniqueness only if conditional
提问by kid_drew
I have a Question class:
我有一个问题类:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id
end
A given user can only create a single question per day, so I want to force uniqueness in the database via a unique index and the Question class via validates_uniqueness_of.
给定的用户每天只能创建一个问题,所以我想通过唯一索引强制数据库中的唯一性,并通过 validates_uniqueness_of 强制问题类。
The trouble I'm running into is that I only want that constraint for non-admin users. So admins can create as many questions per day as they want. Any ideas for how to achieve that elegantly?
我遇到的问题是我只希望对非管理员用户进行限制。因此,管理员可以根据需要每天创建任意数量的问题。关于如何优雅地实现这一目标的任何想法?
回答by Jon
You can make a validation conditional by passing either a simple string of Ruby to be executed, a Proc, or a method name as a symbol as a value to either :ifor :unlessin the options for your validation. Here are some examples:
您可以通过将要执行的简单 Ruby 字符串、Proc 或方法名称作为符号作为值传递给验证选项:if或:unless选项来使验证有条件。这里有些例子:
Prior to Rails version 5.2 you could pass a string:
在 Rails 5.2 版之前,您可以传递一个字符串:
# using a string:
validates :name, uniqueness: true, if: 'name.present?'
From 5.2 onwards, strings are no longer supported, leaving you the following options:
从 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
In your case, you could do something like this:
在您的情况下,您可以执行以下操作:
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
Have a look at the conditional validation section on the rails guides for more details: http://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation
有关更多详细信息,请查看 Rails 指南上的条件验证部分:http: //edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation
回答by Peter Alfvin
The only way I know of to guarantee uniqueness is through the database (e.g. a unique index). All Rails-only based approaches involve race conditions. Given your constraints, I would think the easiest thing would be to establish a separate, uniquely indexed column containing a combination of the day and user id which you'd leave nullfor admins.
我所知道的唯一保证唯一性的方法是通过数据库(例如唯一索引)。所有基于 Rails 的方法都涉及竞争条件。鉴于您的限制,我认为最简单的方法是建立一个单独的、唯一索引的列,其中包含您留给null管理员的日期和用户 ID 的组合。
As for validates_uniqueness_of, you can restrict validation to non-admins through use of an ifor unlessoption, as discussed in http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
至于validates_uniqueness_of,您可以通过使用if或unless选项将验证限制为非管理员,如http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of 中所述
回答by Mohamad
Just add a condition to the validates_uniqueness_ofcall.
只需在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
But even better, just create a custom validation:
但更好的是,只需创建一个自定义验证:
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

