开发者

Rails 3 validate uniqueness of one value against a column?

开发者 https://www.devze.com 2023-03-27 12:26 出处:网络
I have a model with an active column, which is a boolean. I want to validate the uniqueness of all newly added records against company_id, such that I can add as many records as I want with the same c

I have a model with an active column, which is a boolean. I want to validate the uniqueness of all newly added records against company_id, such that I can add as many records as I want with the same company_id to the table so long as active is set to false. There should only be one active record for each company_id.

How would I write this? I've already tried:

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

But that seems to also validate against unique combinations of active being false (such that I can never have more than two company_id's in the table with the same active status, regardless of what active is)--the validation above allows two records for company_id, one with active = false and the other with active = true. Once those two records are in, the validation blocks everything else.

I then tried adding this:

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

But that doesn't seem to have changed the va开发者_JAVA技巧lidation at all (same issue as above).

How can I write this validation so that I can add as many records with the same company_id so long as active is false, and only allowing one active = true per company_id?


No need to use validates_each - that's only if you want to pass multiple attributes through the same block. Just create a custom validation:

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


Rails 4+ offers a much better solution for this.

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

check out documentation


Ok, I think I finally figured this out.

Checking up on Rails Guides led me to validates_each, which led me to this solution:

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

I don't know if that's the most efficient way to write this, but it works. I'm open to any and all suggestions!


Old topic, but there is the simpliest solution. The answer above is almost correct. You can use :if statement as a key like:

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


Using @raj answer with validates method:

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


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

And maybe you want to call company itself

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

EDIT

Following answer based on your solution is a bit more efficient:

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
0

精彩评论

暂无评论...
验证码 换一张
取 消