Rails 3 проверяет уникальность одного значения для столбца?

У меня есть модель с столбцом active, которая является логической. Я хочу проверить уникальность всех вновь добавленных записей в отношении company_id, чтобы я мог добавить столько записей, сколько хочу, с тем же company_id в таблицу, если active установлен на false. Для каждого company_id должна быть только одна запись active.

Как бы я это написал? Я уже пробовал:

validates :company_id, :uniqueness => { :scope => :active }

Но это также подтверждает, что уникальные комбинации active являются false (такие, что я никогда не могу иметь больше двух company_id в таблице с тем же статусом active, вне зависимости от того, что active is) - приведенная выше проверка позволяет использовать две записи для company_id, одну с active = false, а другую - с помощью active = true. После того, как эти две записи включены, валидация блокирует все остальное.

Затем я попытался добавить это:

scope :active, where(:active => true)

Но это, похоже, не изменило валидацию вообще (та же проблема, что и выше).

Как я могу написать эту проверку, чтобы я мог добавить столько записей с тем же company_id, пока active является ложным, и только один active = true за company_id?

Ответы

Ответ 1

Не нужно использовать validates_each - это только если вы хотите передать несколько атрибутов через один и тот же блок. Просто создайте пользовательскую проверку:

validate :company_id_when_active

def company_id_when_active
  if active? and CompanyTerm.exists? ["company_id = ? AND active = 1 AND id != ?", company_id, id.to_i]
    errors.add( :company_id, 'already has an active term')
  end
end

Ответ 2

Rails 4+ предлагает гораздо лучшее решение для этого.

  validates_uniqueness_of :company_id, conditions: -> { where(active: true) }

проверить документация

Ответ 3

Хорошо, я думаю, что, наконец, понял это.

Проверка на Rails Guides привела меня к validates_each, что привело меня к этому решению:

scope :active, where(:active => true)

validates_each :company do |model, attr, value|
  active = CompanyTerm.active.where(:company_id => value)
  model.errors.add(attr, 'already has an active term') unless active.empty?
end

Я не знаю, если это самый эффективный способ написать это, но он работает. Я открыт для любых предложений!

Ответ 4

Старая тема, но есть самое простое решение. Ответ выше почти правильный. Вы можете использовать оператор :if как ключ, например:

validates :active, :uniqueness => { :scope => :company_id }, :if => :active

Ответ 5

validates :company_id, :uniqueness => { :scope => :active } unless Proc.new { self.active }

И, возможно, вы хотите позвонить самой компании

validates :company, :uniqueness => { :scope => :active } unless Proc.new { self.active }

ИЗМЕНИТЬ

Следующий ответ, основанный на вашем решении, немного эффективнее:

scope :active, where(:active => true)

validates_each :company do |model, attr, value|
  model.errors.add(attr, 'already has an active term') if CompanyTerm.active.exists?(:company_id => value)
end