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
Другие вопросы по тегам