44

I'm quite new to Rails and found a little snippet to validate presence and uniqueness step by step: first check presence, then check uniqueness.

validates :email, :presence => true, :allow_blank => true, :uniqueness => { :case_sensitive => false }

I'm a little bit confused about using presence => true and allow_blank => true together.

Without using allow_blank => true both rules will be checked at the same time and not step by step.

Why does allow_blank => true do this magic?

Slevin
  • 4,268
  • 12
  • 40
  • 90

5 Answers5

37

What you've got is equivalent to this (wrapped for clarity):

validates :email, :presence => true, 
            :uniqueness => { :allow_blank => true, :case_sensitive => false }

That's a little silly though since if you're requiring presence, then that's going to "invalidate" the :allow_blank clause to :uniqueness.

It makes more sense when you switch to using other validators.. say... format and uniqueness, but you don't want any checks if it's blank. In this case, adding a "globally applied" :allow_blank makes more sense and DRY's up the code a little bit.

This...

validates :email, :format => {:allow_blank => true, ...}, 
                  :uniqueness => {:allow_blank => true, ...}

can be written like:

validates :email, :allow_blank => true, :format => {...}, :uniqueness => {...}
michaelrbock
  • 1,160
  • 1
  • 11
  • 20
Philip Hallstrom
  • 19,673
  • 2
  • 42
  • 46
  • Thanks a lot! I just found this snippet somewhere on stackoverflow and became curious why someone should use `allow_blank` and `presence` together :) – Slevin Jan 24 '13 at 08:25
  • you might want to use them together to display a different error message for invalid format vs blank for example. If you don't put the allow_blank you'd get an "invalid format" for a blank value – montrealmike Oct 13 '15 at 15:13
31

The following distinction can be useful to know:

presence: true                    # nil and empty string fail validation
presence: true, allow_blank: true # nil fails validation, empty string passes
Dennis
  • 56,821
  • 26
  • 143
  • 139
  • 9
    Is that acutally true? As far as I understand the docs, allow_blank will skip validations on all blank values. `nil` is a blank value. A quick test on Rails 5 tells me that `nil` does not fail my validation... – NobodysNightmare Jul 18 '17 at 11:48
  • 15
    This answer is super WRONG! At least in Rails 5. – dferrazm Oct 16 '17 at 12:54
  • @dferrazm is correct here, you can see by looking at the tests for the presence validator with the `allow_blank` option: https://github.com/rails/rails/blob/0e626670476cb02a71a1bcb61f8b9d0b3315383f/activemodel/test/cases/validations/presence_validation_test.rb#L92-L106 – thrgamon Oct 30 '19 at 02:03
  • Please edit the answer with corrections once you've verified in code what the behaviour is for how `presence` and `allow_blank` work together. I stopped using Rails many years ago otherwise I'd do it myself. I'm relying on the community to keep this answer up to date and reliable. – Dennis Oct 30 '19 at 12:03
  • 1
    To make this work, use `validates :columnx, presence: true, if: Proc.new{ |x| x.columnx.nil? }`, based on https://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation – pgericson Nov 04 '19 at 15:08
7

:allow_blank is an option that will "disable" several of the validators, but not the presence validator. The result of using these two together is that when the field is left blank, you will get the :blank error message (i.e., "can't be blank"), but not the other error messages.

graywh
  • 9,640
  • 2
  • 29
  • 27
4

In your code, :presence => and :uniqueness => are validators, while :allow_blank => is a default option that gets passed to other validators.

So your code:

validates(
    :email,
    :presence => true,
    :allow_blank => true,
    :uniqueness => { :case_sensitive => false }
)

Is equivalent to this code:

validates(
    :email,
    :presence => { :allow_blank => true },
    :uniqueness => { :allow_blank => true, :case_sensitive => false }
)

However, the presence validator ignores the allow_blank option, so your code ends up being essentially this:

validates(
    :email,
    :presence => { }, # `{ }` is equivalent to `true`
    :uniqueness => { :allow_blank => true, :case_sensitive => false }
)

Having :allow_blank => true in :uniqueness means that when the email is blank, the uniqueness validation will not be run.

One effect of this is that you eliminate a DB query.

E.g., without the :allow_blank => true condition you would see this:

>> user = User.new(email: nil)
>> user.valid?
  User Exists (0.2ms) SELECT  1 AS one FROM "users" WHERE "users"."name" IS NULL LIMIT 1
=> false
>> user.errors.messages
=> {:email=>["can't be blank"]}

But with the :allow_blank => true option you won't see that User Exists DB query happen.

Another edge-case side effect happens when you have a record with a blank email address in your DB already. In that case if you don't have the :allow_blank => true option on the uniqueness validator, then you'll see two errors come back:

>> user = User.new(email: nil)
>> user.valid?
  User Exists (0.2ms) SELECT  1 AS one FROM "users" WHERE "users"."name" IS NULL LIMIT 1
=> false
>> user.errors.messages
=> {:email=>["has already been taken", "can't be blank"]}

But with the :allow_blank => true option you'll only see the "can't be blank" error (because the uniqueness validation won't run when the email is blank).

Jordan Brough
  • 6,808
  • 3
  • 31
  • 31
1

from Rails annotation

# * <tt>:allow_nil</tt> - Skip validation if the attribute is +nil+.
# * <tt>:allow_blank</tt> - Skip validation if the attribute is blank.

so, it means when we use allow_blank on email, if the email is nil, only one error added to errors object, jump the uniqueness validates.

cbd Focus
  • 11
  • 3