validates_uniqueness_of passes on nil or blank (without allow_nil and allow_blank)
Asked Answered
F

3

82

The uniqueness validator of ActiveRecord has an options to skip validation if the value is nil or blank. Even if I set both parameters to true (the default behaviour) I can create one record with nil and blank before the validation hits. I use the default SQlite3 Database sqlite3-ruby (1.2.5).

Edit for clarification: I get the expected result if I add validates_presence_of to the Model. I thought that the default behaviour of validates_uniqueness_of would make this redundant.

Testcase:

rails validation_test
cd validation_test/
script/generate Model Thing identification:string
rake db:migrate

Content of app/models/thing.rb:

class Thing < ActiveRecord::Base
  validates_uniqueness_of :identification
end

Rails console:

script/console 
Loading development environment (Rails 2.3.4)
>> Thing.create!
=> #<Thing id: 1, identification: nil, created_at: "2009-09-26 01:49:32", updated_at: "2009-09-26 01:49:32">
>> Thing.create! :identification => ""
=> #<Thing id: 2, identification: "", created_at: "2009-09-26 01:49:42", updated_at: "2009-09-26 01:49:42">
>> Thing.create! :identification => ""
ActiveRecord::RecordInvalid: Validation failed: Identification has already been taken
    from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/validations.rb:1090:in `save_without_dirty!'
    from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/dirty.rb:87:in `save_without_transactions!'
    from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/transactions.rb:200:in `save!'
    from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/connection_adapters/abstract/database_statements.rb:136:in `transaction'
    from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/transactions.rb:182:in `transaction'
    from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/transactions.rb:200:in `save!'
    from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/transactions.rb:208:in `rollback_active_record_state!'
    from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/transactions.rb:200:in `save!'
    from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/validations.rb:1059:in `create!'
    from (irb):3
>> Thing.count
=> 2

Why do the first two creations pass?

Thanks

Falcone answered 26/9, 2009 at 2:1 Comment(2)
+1 because this is a model of how to ask a question. You clearly stated what you did, what you saw, and what you expected.Ance
Thanks. But it seems like this does not prevent from being misunderstood. :)Dockhand
A
133

You are mistaken about the default behavior. From the docs:

:allow_nil - If set to true, skips this validation if the attribute is nil (default is false). :allow_blank - If set to true, skips this validation if the attribute is blank (default is false, it includes nil too).

Setting allow_blank to true, I see the following behavior with Rails 2.3.4.

class Thing < ActiveRecord::Base
  validates_uniqueness_of :identification, :allow_blank => true
end

>> Thing.create! :identification => ""
=> #<Thing id: 6, identification: "", created_at: "2009-09-26 03:09:48", updated_at: "2009-09-26 03:09:48">
>> Thing.create! :identification => ""
=> #<Thing id: 7, identification: "", created_at: "2009-09-26 03:09:49", updated_at: "2009-09-26 03:09:49">
>> Thing.create! :identification => nil
=> #<Thing id: 8, identification: nil, created_at: "2009-09-26 03:09:52", updated_at: "2009-09-26 03:09:52">
>> Thing.create! :identification => nil
=> #<Thing id: 9, identification: nil, created_at: "2009-09-26 03:09:53", updated_at: "2009-09-26 03:09:53">

Edit: Addressing your clarification.

Adding a validates_presence_of would be correct for what you're trying to do. It's not redundant, since it's checking for a completely different error case. It also has its own error message, which will be important for the user.

class Thing < ActiveRecord::Base
  validates_uniqueness_of :identification, :allow_blank => true
  validates_presence_of :identification
end
Ance answered 26/9, 2009 at 3:11 Comment(3)
Actually I want it the other way around. I want to prevent nil and blank values completely. Like an additional validates_presence_of would do. But I thought the validates_presence would be redundant if there is already a validates_uniqueness_of validator.Dockhand
Okay now this makes sense. Even a null and blank can in fact be unique. If i want to prevent empty values I have to explicitly say this. First I i found it irritating. Now,on a second thought, I even like it more that way. I think I just got my thinking another very little step into the right direction. Thanks.Dockhand
:allow_blank => true includes nil values. So an additional :allow_nil => true is redundant.Postglacial
L
14

and with validates:

validates :email, uniqueness: { allow_blank: true }
# or
validates :email, uniqueness: { allow_nil: true }
Luster answered 26/9, 2009 at 2:1 Comment(0)
T
0
class Thing < ActiveRecord::Base
  validates :identification, uniqueness: true, allow_nil: true
end
Timecard answered 26/9, 2009 at 2:1 Comment(1)
While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.Vilma

© 2022 - 2024 — McMap. All rights reserved.