Reputation: 4222
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?
Upvotes: 45
Views: 38533
Reputation: 7195
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, presence
is the opposite of blank
, so your code is actually equivalent to this code:
validates(
:email,
:uniqueness => { :allow_blank => true, :case_sensitive => false }
)
NOTE: In Rails v3 the presence
validator ignored the allow_blank
option (see here).
I think what you really would want is this:
validates(
:email,
:presence,
:uniqueness => { :allow_blank => true, :case_sensitive => false }
)
Having :allow_blank => true
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).
Upvotes: 3
Reputation: 10181
Common options are distributed to each validators. https://github.com/rails/rails/blob/v5.0.0/activemodel/lib/active_model/validations/validates.rb#L94C1-L103C91
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 }
)
In this case, PresenceValidator and UniquenessValidator receive these options.
Both do not override EachValidator#validate
which process allow_nil
allow_blank
options.
https://github.com/rails/rails/blob/main/activemodel/lib/active_model/validator.rb#L150C5-L157C8
def validate(record)
attributes.each do |attribute|
value = record.read_attribute_for_validation(attribute)
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
value = prepare_value_for_validation(value, record, attribute)
validate_each(record, attribute, value)
end
end
Upvotes: 0
Reputation: 19879
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 => {...}
Upvotes: 36
Reputation: 11
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.
Upvotes: 1
Reputation: 59529
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
Upvotes: 33
Reputation: 9850
: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.
Upvotes: 7