Rails проверяет уникальность, только если она условна
У меня есть вопрос класса:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id
end
Данный пользователь может создавать только один вопрос в день, поэтому я хочу навязать уникальность в базе данных с помощью уникального индекса и класса Question с помощью validates_uniqueness_of.
Проблема, с которой я сталкиваюсь, заключается в том, что я хочу использовать это ограничение только для пользователей без прав администратора. Таким образом, администраторы могут создавать столько вопросов в день, сколько они хотят. Есть идеи, как этого добиться?
3 ответа
Вы можете сделать проверку условной, передав либо простую строку 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
Посмотрите на раздел условной проверки на направляющих рельсов для более подробной информации: http://edgeguides.rubyonrails.org/active_record_validations.html
Единственный известный мне способ гарантировать уникальность - через базу данных (например, уникальный индекс). Все подходы, основанные только на Rails, включают условия гонки. Учитывая ваши ограничения, я думаю, что проще всего было бы создать отдельный столбец с уникальной индексацией, содержащий комбинацию дня и идентификатора пользователя, который вы бы оставили null
для админов.
Что касается validates_uniqueness_of
, вы можете ограничить валидацию для не-администраторов с помощью if
или же unless
вариант, как описано в http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
Просто добавьте условие к 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