Подтвердить что ты не робот

Рельсы проверяют уникальность только при условии

У меня есть класс Question:

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

  validates_uniqueness_of :created_on, :scope => :user_id
end

Данный пользователь может создать только один вопрос в день, поэтому я хочу заставить уникальность в базе данных через уникальный индекс и класс Question через validates_uniqueness_of.

Проблема, с которой я сталкиваюсь, заключается в том, что я хочу только этого ограничения для пользователей, не являющихся администраторами. Поэтому администраторы могут создавать столько вопросов в день, сколько захотят. Любые идеи о том, как добиться этого элегантно?

4b9b3361

Ответ 1

Вы можете сделать условие проверки, передав либо простую строку Ruby для выполнения, либо Proc, либо имя метода в качестве символа в качестве значения для :if или :unless в параметрах вашей проверки. Вот несколько примеров:

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

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

# using 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#conditional-validation

Ответ 2

Единственный способ гарантировать уникальность - через базу данных (например, уникальный индекс). Все подходы, основанные только на Rails, включают условия гонки. Учитывая ваши ограничения, я бы подумал, что проще всего создать отдельный, однозначно индексированный столбец, содержащий комбинацию дня и идентификатора пользователя, которые вы оставили бы null для админов.

Что касается validates_uniqueness_of, вы можете ограничить проверку не-админами с помощью опции if или unless, как описано в http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of

Ответ 3

Просто добавьте условие к вызову 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