965 lines
31 KiB
Markdown
965 lines
31 KiB
Markdown
Active Record Validations
|
|
=========================
|
|
|
|
This guide teaches you how to validate the state of objects before they go into
|
|
the database using Active Record's validations feature.
|
|
|
|
After reading this guide, you will know:
|
|
|
|
* How to use the built-in Active Record validation helpers.
|
|
* How to create your own custom validation methods.
|
|
* How to work with the error messages generated by the validation process.
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
Validations Overview
|
|
--------------------
|
|
|
|
Here's an example of a very simple validation:
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :name, presence: true
|
|
end
|
|
|
|
Person.create(name: "John Doe").valid? # => true
|
|
Person.create(name: nil).valid? # => false
|
|
```
|
|
|
|
As you can see, our validation lets us know that our `Person` is not valid
|
|
without a `name` attribute. The second `Person` will not be persisted to the
|
|
database.
|
|
|
|
Before we dig into more details, let's talk about how validations fit into the
|
|
big picture of your application.
|
|
|
|
### Why Use Validations?
|
|
|
|
Validations are used to ensure that only valid data is saved into your
|
|
database. For example, it may be important to your application to ensure that
|
|
every user provides a valid email address and mailing address. Model-level
|
|
validations are the best way to ensure that only valid data is saved into your
|
|
database. They are database agnostic, cannot be bypassed by end users, and are
|
|
convenient to test and maintain. Rails makes them easy to use, provides
|
|
built-in helpers for common needs, and allows you to create your own validation
|
|
methods as well.
|
|
|
|
There are several other ways to validate data before it is saved into your
|
|
database, including native database constraints, client-side validations,
|
|
controller-level validations. Here's a summary of the pros and cons:
|
|
|
|
* Database constraints and/or stored procedures make the validation mechanisms
|
|
database-dependent and can make testing and maintenance more difficult.
|
|
However, if your database is used by other applications, it may be a good
|
|
idea to use some constraints at the database level. Additionally,
|
|
database-level validations can safely handle some things (such as uniqueness
|
|
in heavily-used tables) that can be difficult to implement otherwise.
|
|
* Client-side validations can be useful, but are generally unreliable if used
|
|
alone. If they are implemented using JavaScript, they may be bypassed if
|
|
JavaScript is turned off in the user's browser. However, if combined with
|
|
other techniques, client-side validation can be a convenient way to provide
|
|
users with immediate feedback as they use your site.
|
|
* Controller-level validations can be tempting to use, but often become
|
|
unwieldy and difficult to test and maintain. Whenever possible, it's a good
|
|
idea to keep your controllers skinny, as it will make your application a
|
|
pleasure to work with in the long run.
|
|
|
|
Choose these in certain, specific cases. It's the opinion of the Rails team
|
|
that model-level validations are the most appropriate in most circumstances.
|
|
|
|
### When Does Validation Happen?
|
|
|
|
There are two kinds of Active Record objects: those that correspond to a row
|
|
inside your database and those that do not. When you create a fresh object, for
|
|
example using the `new` method, that object does not belong to the database
|
|
yet. Once you call `save` upon that object it will be saved into the
|
|
appropriate database table. Active Record uses the `new_record?` instance
|
|
method to determine whether an object is already in the database or not.
|
|
Consider the following simple Active Record class:
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
end
|
|
```
|
|
|
|
We can see how it works by looking at some `rails console` output:
|
|
|
|
```ruby
|
|
$ rails console
|
|
>> p = Person.new(name: "John Doe")
|
|
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>
|
|
>> p.new_record?
|
|
=> true
|
|
>> p.save
|
|
=> true
|
|
>> p.new_record?
|
|
=> false
|
|
```
|
|
|
|
Creating and saving a new record will send an SQL `INSERT` operation to the
|
|
database. Updating an existing record will send an SQL `UPDATE` operation
|
|
instead. Validations are typically run before these commands are sent to the
|
|
database. If any validations fail, the object will be marked as invalid and
|
|
Active Record will not perform the `INSERT` or `UPDATE` operation. This avoids
|
|
storing an invalid object in the database. You can choose to have specific
|
|
validations run when an object is created, saved, or updated.
|
|
|
|
CAUTION: There are many ways to change the state of an object in the database.
|
|
Some methods will trigger validations, but some will not. This means that it's
|
|
possible to save an object in the database in an invalid state if you aren't
|
|
careful.
|
|
|
|
The following methods trigger validations, and will save the object to the
|
|
database only if the object is valid:
|
|
|
|
* `create`
|
|
* `create!`
|
|
* `save`
|
|
* `save!`
|
|
* `update`
|
|
* `update_attributes`
|
|
* `update_attributes!`
|
|
|
|
The bang versions (e.g. `save!`) raise an exception if the record is invalid.
|
|
The non-bang versions don't: `save` and `update_attributes` return `false`,
|
|
`create` and `update` just return the objects.
|
|
|
|
### Skipping Validations
|
|
|
|
The following methods skip validations, and will save the object to the
|
|
database regardless of its validity. They should be used with caution.
|
|
|
|
* `decrement!`
|
|
* `decrement_counter`
|
|
* `increment!`
|
|
* `increment_counter`
|
|
* `toggle!`
|
|
* `touch`
|
|
* `update_all`
|
|
* `update_attribute`
|
|
* `update_column`
|
|
* `update_columns`
|
|
* `update_counters`
|
|
|
|
Note that `save` also has the ability to skip validations if passed `validate:
|
|
false` as argument. This technique should be used with caution.
|
|
|
|
* `save(validate: false)`
|
|
|
|
### `valid?` and `invalid?`
|
|
|
|
To verify whether or not an object is valid, Rails uses the `valid?` method.
|
|
You can also use this method on your own. `valid?` triggers your validations
|
|
and returns true if no errors were found in the object, and false otherwise.
|
|
As you saw above:
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :name, presence: true
|
|
end
|
|
|
|
Person.create(name: "John Doe").valid? # => true
|
|
Person.create(name: nil).valid? # => false
|
|
```
|
|
|
|
After Active Record has performed validations, any errors found can be accessed
|
|
through the `errors` instance method, which returns a collection of errors. By
|
|
definition, an object is valid if this collection is empty after running
|
|
validations.
|
|
|
|
Note that an object instantiated with `new` will not report errors even if it's
|
|
technically invalid, because validations are not run when using `new`.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :name, presence: true
|
|
end
|
|
|
|
>> p = Person.new
|
|
#=> #<Person id: nil, name: nil>
|
|
>> p.errors
|
|
#=> {}
|
|
|
|
>> p.valid?
|
|
#=> false
|
|
>> p.errors
|
|
#=> {name:["can't be blank"]}
|
|
|
|
>> p = Person.create
|
|
#=> #<Person id: nil, name: nil>
|
|
>> p.errors
|
|
#=> {name:["can't be blank"]}
|
|
|
|
>> p.save
|
|
#=> false
|
|
|
|
>> p.save!
|
|
#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
|
|
|
>> Person.create!
|
|
#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
|
```
|
|
|
|
`invalid?` is simply the inverse of `valid?`. It triggers your validations,
|
|
returning true if any errors were found in the object, and false otherwise.
|
|
|
|
### `errors[]`
|
|
|
|
To verify whether or not a particular attribute of an object is valid, you can
|
|
use `errors[:attribute]`. It returns an array of all the errors for
|
|
`:attribute`. If there are no errors on the specified attribute, an empty array
|
|
is returned.
|
|
|
|
This method is only useful _after_ validations have been run, because it only
|
|
inspects the errors collection and does not trigger validations itself. It's
|
|
different from the `ActiveRecord::Base#invalid?` method explained above because
|
|
it doesn't verify the validity of the object as a whole. It only checks to see
|
|
whether there are errors found on an individual attribute of the object.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :name, presence: true
|
|
end
|
|
|
|
>> Person.new.errors[:name].any? # => false
|
|
>> Person.create.errors[:name].any? # => true
|
|
```
|
|
|
|
We'll cover validation errors in greater depth in the [Working with Validation
|
|
Errors](#working-with-validation-errors) section. For now, let's turn to the
|
|
built-in validation helpers that Rails provides by default.
|
|
|
|
Validation Helpers
|
|
------------------
|
|
|
|
Active Record offers many pre-defined validation helpers that you can use
|
|
directly inside your class definitions. These helpers provide common validation
|
|
rules. Every time a validation fails, an error message is added to the object's
|
|
`errors` collection, and this message is associated with the attribute being
|
|
validated.
|
|
|
|
Each helper accepts an arbitrary number of attribute names, so with a single
|
|
line of code you can add the same kind of validation to several attributes.
|
|
|
|
All of them accept the `:on` and `:message` options, which define when the
|
|
validation should be run and what message should be added to the `errors`
|
|
collection if it fails, respectively. The `:on` option takes one of the values
|
|
`:save` (the default), `:create` or `:update`. There is a default error
|
|
message for each one of the validation helpers. These messages are used when
|
|
the `:message` option isn't specified. Let's take a look at each one of the
|
|
available helpers.
|
|
|
|
### `acceptance`
|
|
|
|
This method validates that a checkbox on the user interface was checked when a
|
|
form was submitted. This is typically used when the user needs to agree to your
|
|
application's terms of service, confirm reading some text, or any similar
|
|
concept. This validation is very specific to web applications and this
|
|
'acceptance' does not need to be recorded anywhere in your database (if you
|
|
don't have a field for it, the helper will just create a virtual attribute).
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :terms_of_service, acceptance: true
|
|
end
|
|
```
|
|
|
|
The default error message for this helper is _"must be accepted"_.
|
|
|
|
It can receive an `:accept` option, which determines the value that will be
|
|
considered acceptance. It defaults to "1" and can be easily changed.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :terms_of_service, acceptance: { accept: 'yes' }
|
|
end
|
|
```
|
|
|
|
### `validates_associated`
|
|
|
|
You should use this helper when your model has associations with other models
|
|
and they also need to be validated. When you try to save your object, `valid?`
|
|
will be called upon each one of the associated objects.
|
|
|
|
```ruby
|
|
class Library < ActiveRecord::Base
|
|
has_many :books
|
|
validates_associated :books
|
|
end
|
|
```
|
|
|
|
This validation will work with all of the association types.
|
|
|
|
CAUTION: Don't use `validates_associated` on both ends of your associations.
|
|
They would call each other in an infinite loop.
|
|
|
|
The default error message for `validates_associated` is _"is invalid"_. Note
|
|
that each associated object will contain its own `errors` collection; errors do
|
|
not bubble up to the calling model.
|
|
|
|
### `confirmation`
|
|
|
|
You should use this helper when you have two text fields that should receive
|
|
exactly the same content. For example, you may want to confirm an email address
|
|
or a password. This validation creates a virtual attribute whose name is the
|
|
name of the field that has to be confirmed with "_confirmation" appended.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :email, confirmation: true
|
|
end
|
|
```
|
|
|
|
In your view template you could use something like
|
|
|
|
```erb
|
|
<%= text_field :person, :email %>
|
|
<%= text_field :person, :email_confirmation %>
|
|
```
|
|
|
|
This check is performed only if `email_confirmation` is not `nil`. To require
|
|
confirmation, make sure to add a presence check for the confirmation attribute
|
|
(we'll take a look at `presence` later on this guide):
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :email, confirmation: true
|
|
validates :email_confirmation, presence: true
|
|
end
|
|
```
|
|
|
|
The default error message for this helper is _"doesn't match confirmation"_.
|
|
|
|
### `exclusion`
|
|
|
|
This helper validates that the attributes' values are not included in a given
|
|
set. In fact, this set can be any enumerable object.
|
|
|
|
```ruby
|
|
class Account < ActiveRecord::Base
|
|
validates :subdomain, exclusion: { in: %w(www us ca jp),
|
|
message: "Subdomain %{value} is reserved." }
|
|
end
|
|
```
|
|
|
|
The `exclusion` helper has an option `:in` that receives the set of values that
|
|
will not be accepted for the validated attributes. The `:in` option has an
|
|
alias called `:within` that you can use for the same purpose, if you'd like to.
|
|
This example uses the `:message` option to show how you can include the
|
|
attribute's value.
|
|
|
|
The default error message is _"is reserved"_.
|
|
|
|
### `format`
|
|
|
|
This helper validates the attributes' values by testing whether they match a
|
|
given regular expression, which is specified using the `:with` option.
|
|
|
|
```ruby
|
|
class Product < ActiveRecord::Base
|
|
validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
|
|
message: "Only letters allowed" }
|
|
end
|
|
```
|
|
|
|
The default error message is _"is invalid"_.
|
|
|
|
### `inclusion`
|
|
|
|
This helper validates that the attributes' values are included in a given set.
|
|
In fact, this set can be any enumerable object.
|
|
|
|
```ruby
|
|
class Coffee < ActiveRecord::Base
|
|
validates :size, inclusion: { in: %w(small medium large),
|
|
message: "%{value} is not a valid size" }
|
|
end
|
|
```
|
|
|
|
The `inclusion` helper has an option `:in` that receives the set of values that
|
|
will be accepted. The `:in` option has an alias called `:within` that you can
|
|
use for the same purpose, if you'd like to. The previous example uses the
|
|
`:message` option to show how you can include the attribute's value.
|
|
|
|
The default error message for this helper is _"is not included in the list"_.
|
|
|
|
### `length`
|
|
|
|
This helper validates the length of the attributes' values. It provides a
|
|
variety of options, so you can specify length constraints in different ways:
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :name, length: { minimum: 2 }
|
|
validates :bio, length: { maximum: 500 }
|
|
validates :password, length: { in: 6..20 }
|
|
validates :registration_number, length: { is: 6 }
|
|
end
|
|
```
|
|
|
|
The possible length constraint options are:
|
|
|
|
* `:minimum` - The attribute cannot have less than the specified length.
|
|
* `:maximum` - The attribute cannot have more than the specified length.
|
|
* `:in` (or `:within`) - The attribute length must be included in a given
|
|
interval. The value for this option must be a range.
|
|
* `:is` - The attribute length must be equal to the given value.
|
|
|
|
The default error messages depend on the type of length validation being
|
|
performed. You can personalize these messages using the `:wrong_length`,
|
|
`:too_long`, and `:too_short` options and `%{count}` as a placeholder for the
|
|
number corresponding to the length constraint being used. You can still use the
|
|
`:message` option to specify an error message.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :bio, length: { maximum: 1000,
|
|
too_long: "%{count} characters is the maximum allowed" }
|
|
end
|
|
```
|
|
|
|
This helper counts characters by default, but you can split the value in a
|
|
different way using the `:tokenizer` option:
|
|
|
|
```ruby
|
|
class Essay < ActiveRecord::Base
|
|
validates :content, length: {
|
|
minimum: 300,
|
|
maximum: 400,
|
|
tokenizer: lambda { |str| str.scan(/\w+/) },
|
|
too_short: "must have at least %{count} words",
|
|
too_long: "must have at most %{count} words"
|
|
}
|
|
end
|
|
```
|
|
|
|
Note that the default error messages are plural (e.g., "is too short (minimum
|
|
is %{count} characters)"). For this reason, when `:minimum` is 1 you should
|
|
provide a personalized message or use `validates_presence_of` instead. When
|
|
`:in` or `:within` have a lower limit of 1, you should either provide a
|
|
personalized message or call `presence` prior to `length`.
|
|
|
|
The `size` helper is an alias for `length`.
|
|
|
|
### `numericality`
|
|
|
|
This helper validates that your attributes have only numeric values. By
|
|
default, it will match an optional sign followed by an integral or floating
|
|
point number. To specify that only integral numbers are allowed set
|
|
`:only_integer` to true.
|
|
|
|
If you set `:only_integer` to `true`, then it will use the
|
|
|
|
```ruby
|
|
/\A[+-]?\d+\Z/
|
|
```
|
|
|
|
regular expression to validate the attribute's value. Otherwise, it will try to
|
|
convert the value to a number using `Float`.
|
|
|
|
WARNING. Note that the regular expression above allows a trailing newline
|
|
character.
|
|
|
|
```ruby
|
|
class Player < ActiveRecord::Base
|
|
validates :points, numericality: true
|
|
validates :games_played, numericality: { only_integer: true }
|
|
end
|
|
```
|
|
|
|
Besides `:only_integer`, this helper also accepts the following options to add
|
|
constraints to acceptable values:
|
|
|
|
* `:greater_than` - Specifies the value must be greater than the supplied
|
|
value. The default error message for this option is _"must be greater than
|
|
%{count}"_.
|
|
* `:greater_than_or_equal_to` - Specifies the value must be greater than or
|
|
equal to the supplied value. The default error message for this option is
|
|
_"must be greater than or equal to %{count}"_.
|
|
* `:equal_to` - Specifies the value must be equal to the supplied value. The
|
|
default error message for this option is _"must be equal to %{count}"_.
|
|
* `:less_than` - Specifies the value must be less than the supplied value. The
|
|
default error message for this option is _"must be less than %{count}"_.
|
|
* `:less_than_or_equal_to` - Specifies the value must be less than or equal the
|
|
supplied value. The default error message for this option is _"must be less
|
|
than or equal to %{count}"_.
|
|
* `:odd` - Specifies the value must be an odd number if set to true. The
|
|
default error message for this option is _"must be odd"_.
|
|
* `:even` - Specifies the value must be an even number if set to true. The
|
|
default error message for this option is _"must be even"_.
|
|
|
|
The default error message is _"is not a number"_.
|
|
|
|
### `presence`
|
|
|
|
This helper validates that the specified attributes are not empty. It uses the
|
|
`blank?` method to check if the value is either `nil` or a blank string, that
|
|
is, a string that is either empty or consists of whitespace.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :name, :login, :email, presence: true
|
|
end
|
|
```
|
|
|
|
If you want to be sure that an association is present, you'll need to test
|
|
whether the associated object itself is present, and not the foreign key used
|
|
to map the association.
|
|
|
|
```ruby
|
|
class LineItem < ActiveRecord::Base
|
|
belongs_to :order
|
|
validates :order, presence: true
|
|
end
|
|
```
|
|
|
|
In order to validate associated records whose presence is required, you must
|
|
specify the `:inverse_of` the association:
|
|
|
|
```ruby
|
|
class Order < ActiveRecord::Base
|
|
has_many :line_items, inverse_of: :order
|
|
end
|
|
```
|
|
|
|
If you validate the presence of an object associated via a `has_one` or
|
|
`has_many` relationship, it will check that the object is neither `blank?` nor
|
|
`marked_for_destruction?`.
|
|
|
|
Since `false.blank?` is true, if you want to validate the presence of a boolean
|
|
field you should use `validates :field_name, inclusion: { in: [true, false] }`.
|
|
|
|
The default error message is _"can't be empty"_.
|
|
|
|
### `uniqueness`
|
|
|
|
This helper validates that the attribute's value is unique right before the
|
|
object gets saved. It does not create a uniqueness constraint in the database,
|
|
so it may happen that two different database connections create two records
|
|
with the same value for a column that you intend to be unique. To avoid that,
|
|
you must create a unique index in your database.
|
|
|
|
```ruby
|
|
class Account < ActiveRecord::Base
|
|
validates :email, uniqueness: true
|
|
end
|
|
```
|
|
|
|
The validation happens by performing an SQL query into the model's table,
|
|
searching for an existing record with the same value in that attribute.
|
|
|
|
There is a `:scope` option that you can use to specify other attributes that
|
|
are used to limit the uniqueness check:
|
|
|
|
```ruby
|
|
class Holiday < ActiveRecord::Base
|
|
validates :name, uniqueness: { scope: :year,
|
|
message: "should happen once per year" }
|
|
end
|
|
```
|
|
|
|
There is also a `:case_sensitive` option that you can use to define whether the
|
|
uniqueness constraint will be case sensitive or not. This option defaults to
|
|
true.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :name, uniqueness: { case_sensitive: false }
|
|
end
|
|
```
|
|
|
|
WARNING. Note that some databases are configured to perform case-insensitive
|
|
searches anyway.
|
|
|
|
The default error message is _"has already been taken"_.
|
|
|
|
### `validates_with`
|
|
|
|
This helper passes the record to a separate class for validation.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates_with GoodnessValidator
|
|
end
|
|
|
|
class GoodnessValidator < ActiveModel::Validator
|
|
def validate(record)
|
|
if record.first_name == "Evil"
|
|
record.errors[:base] << "This person is evil"
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
NOTE: Errors added to `record.errors[:base]` relate to the state of the record
|
|
as a whole, and not to a specific attribute.
|
|
|
|
The `validates_with` helper takes a class, or a list of classes to use for
|
|
validation. There is no default error message for `validates_with`. You must
|
|
manually add errors to the record's errors collection in the validator class.
|
|
|
|
To implement the validate method, you must have a `record` parameter defined,
|
|
which is the record to be validated.
|
|
|
|
Like all other validations, `validates_with` takes the `:if`, `:unless` and
|
|
`:on` options. If you pass any other options, it will send those options to the
|
|
validator class as `options`:
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates_with GoodnessValidator, fields: [:first_name, :last_name]
|
|
end
|
|
|
|
class GoodnessValidator < ActiveModel::Validator
|
|
def validate(record)
|
|
if options[:fields].any?{|field| record.send(field) == "Evil" }
|
|
record.errors[:base] << "This person is evil"
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
### `validates_each`
|
|
|
|
This helper validates attributes against a block. It doesn't have a predefined
|
|
validation function. You should create one using a block, and every attribute
|
|
passed to `validates_each` will be tested against it. In the following example,
|
|
we don't want names and surnames to begin with lower case.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates_each :name, :surname do |record, attr, value|
|
|
record.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/
|
|
end
|
|
end
|
|
```
|
|
|
|
The block receives the record, the attribute's name and the attribute's value.
|
|
You can do anything you like to check for valid data within the block. If your
|
|
validation fails, you should add an error message to the model, therefore
|
|
making it invalid.
|
|
|
|
Common Validation Options
|
|
-------------------------
|
|
|
|
These are common validation options:
|
|
|
|
### `:allow_nil`
|
|
|
|
The `:allow_nil` option skips the validation when the value being validated is
|
|
`nil`.
|
|
|
|
```ruby
|
|
class Coffee < ActiveRecord::Base
|
|
validates :size, inclusion: { in: %w(small medium large),
|
|
message: "%{value} is not a valid size" }, allow_nil: true
|
|
end
|
|
```
|
|
|
|
TIP: `:allow_nil` is ignored by the presence validator.
|
|
|
|
### `:allow_blank`
|
|
|
|
The `:allow_blank` option is similar to the `:allow_nil` option. This option
|
|
will let validation pass if the attribute's value is `blank?`, like `nil` or an
|
|
empty string for example.
|
|
|
|
```ruby
|
|
class Topic < ActiveRecord::Base
|
|
validates :title, length: { is: 5 }, allow_blank: true
|
|
end
|
|
|
|
Topic.create("title" => "").valid? # => true
|
|
Topic.create("title" => nil).valid? # => true
|
|
```
|
|
|
|
TIP: `:allow_blank` is ignored by the presence validator.
|
|
|
|
### `:message`
|
|
|
|
As you've already seen, the `:message` option lets you specify the message that
|
|
will be added to the `errors` collection when validation fails. When this
|
|
option is not used, Active Record will use the respective default error message
|
|
for each validation helper.
|
|
|
|
### `:on`
|
|
|
|
The `:on` option lets you specify when the validation should happen. The
|
|
default behavior for all the built-in validation helpers is to be run on save
|
|
(both when you're creating a new record and when you're updating it). If you
|
|
want to change it, you can use `on: :create` to run the validation only when a
|
|
new record is created or `on: :update` to run the validation only when a record
|
|
is updated.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
# it will be possible to update email with a duplicated value
|
|
validates :email, uniqueness: true, on: :create
|
|
|
|
# it will be possible to create the record with a non-numerical age
|
|
validates :age, numericality: true, on: :update
|
|
|
|
# the default (validates on both create and update)
|
|
validates :name, presence: true, on: :save
|
|
end
|
|
```
|
|
|
|
Strict Validations
|
|
------------------
|
|
|
|
You can also specify validations to be strict and raise
|
|
`ActiveModel::StrictValidationFailed` when the object is invalid.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :name, presence: { strict: true }
|
|
end
|
|
|
|
Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
|
|
```
|
|
|
|
There is also an ability to pass custom exception to `:strict` option
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
|
|
end
|
|
|
|
Person.new.valid? # => TokenGenerationException: Token can't be blank
|
|
```
|
|
|
|
Conditional Validation
|
|
----------------------
|
|
|
|
Sometimes it will make sense to validate an object only when a given predicate
|
|
is satisfied. You can do that by using the `:if` and `:unless` options, which
|
|
can take a symbol, a string, a `Proc` or an `Array`. You may use the `:if`
|
|
option when you want to specify when the validation **should** happen. If you
|
|
want to specify when the validation **should not** happen, then you may use the
|
|
`:unless` option.
|
|
|
|
### Using a Symbol with `:if` and `:unless`
|
|
|
|
You can associate the `:if` and `:unless` options with a symbol corresponding
|
|
to the name of a method that will get called right before validation happens.
|
|
This is the most commonly used option.
|
|
|
|
```ruby
|
|
class Order < ActiveRecord::Base
|
|
validates :card_number, presence: true, if: :paid_with_card?
|
|
|
|
def paid_with_card?
|
|
payment_type == "card"
|
|
end
|
|
end
|
|
```
|
|
|
|
### Using a String with `:if` and `:unless`
|
|
|
|
You can also use a string that will be evaluated using `eval` and needs to
|
|
contain valid Ruby code. You should use this option only when the string
|
|
represents a really short condition.
|
|
|
|
```ruby
|
|
class Person < ActiveRecord::Base
|
|
validates :surname, presence: true, if: "name.nil?"
|
|
end
|
|
```
|
|
|
|
### Using a Proc with `:if` and `:unless`
|
|
|
|
Finally, it's possible to associate `:if` and `:unless` with a `Proc` object
|
|
which will be called. Using a `Proc` object gives you the ability to write an
|
|
inline condition instead of a separate method. This option is best suited for
|
|
one-liners.
|
|
|
|
```ruby
|
|
class Account < ActiveRecord::Base
|
|
validates :password, confirmation: true,
|
|
unless: Proc.new { |a| a.password.blank? }
|
|
end
|
|
```
|
|
|
|
### Grouping Conditional validations
|
|
|
|
Sometimes it is useful to have multiple validations use one condition, it can
|
|
be easily achieved using `with_options`.
|
|
|
|
```ruby
|
|
class User < ActiveRecord::Base
|
|
with_options if: :is_admin? do |admin|
|
|
admin.validates :password, length: { minimum: 10 }
|
|
admin.validates :email, presence: true
|
|
end
|
|
end
|
|
```
|
|
|
|
All validations inside of `with_options` block will have automatically passed
|
|
the condition `if: :is_admin?`
|
|
|
|
### Combining Validation Conditions
|
|
|
|
On the other hand, when multiple conditions define whether or not a validation
|
|
should happen, an `Array` can be used. Moreover, you can apply both `:if` and
|
|
`:unless` to the same validation.
|
|
|
|
```ruby
|
|
class Computer < ActiveRecord::Base
|
|
validates :mouse, presence: true,
|
|
if: ["market.retail?", :desktop?]
|
|
unless: Proc.new { |c| c.trackpad.present? }
|
|
end
|
|
```
|
|
|
|
The validation only runs when all the `:if` conditions and none of the
|
|
`:unless` conditions are evaluated to `true`.
|
|
|
|
Performing Custom Validations
|
|
-----------------------------
|
|
|
|
When the built-in validation helpers are not enough for your needs, you can
|
|
write your own validators or validation methods as you prefer.
|
|
|
|
### Custom Validators
|
|
|
|
Custom validators are classes that extend `ActiveModel::Validator`. These
|
|
classes must implement a `validate` method which takes a record as an argument
|
|
and performs the validation on it. The custom validator is called using the
|
|
`validates_with` method.
|
|
|
|
```ruby
|
|
class MyValidator < ActiveModel::Validator
|
|
def validate(record)
|
|
unless record.name.starts_with? 'X'
|
|
record.errors[:name] << 'Need a name starting with X please!'
|
|
end
|
|
end
|
|
end
|
|
|
|
class Person
|
|
include ActiveModel::Validations
|
|
validates_with MyValidator
|
|
end
|
|
```
|
|
|
|
The easiest way to add custom validators for validating individual attributes
|
|
is with the convenient `ActiveModel::EachValidator`. In this case, the custom
|
|
validator class must implement a `validate_each` method which takes three
|
|
arguments: record, attribute and value which correspond to the instance, the
|
|
attribute to be validated and the value of the attribute in the passed
|
|
instance.
|
|
|
|
```ruby
|
|
class EmailValidator < ActiveModel::EachValidator
|
|
def validate_each(record, attribute, value)
|
|
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
|
|
record.errors[attribute] << (options[:message] || "is not an email")
|
|
end
|
|
end
|
|
end
|
|
|
|
class Person < ActiveRecord::Base
|
|
validates :email, presence: true, email: true
|
|
end
|
|
```
|
|
|
|
As shown in the example, you can also combine standard validations with your
|
|
own custom validators.
|
|
|
|
### Custom Methods
|
|
|
|
You can also create methods that verify the state of your models and add
|
|
messages to the `errors` collection when they are invalid. You must then
|
|
register these methods by using the `validate` class method, passing in the
|
|
symbols for the validation methods' names.
|
|
|
|
You can pass more than one symbol for each class method and the respective
|
|
validations will be run in the same order as they were registered.
|
|
|
|
```ruby
|
|
class Invoice < ActiveRecord::Base
|
|
validate :expiration_date_cannot_be_in_the_past,
|
|
:discount_cannot_be_greater_than_total_value
|
|
|
|
def expiration_date_cannot_be_in_the_past
|
|
if expiration_date.present? && expiration_date < Date.today
|
|
errors.add(:expiration_date, "can't be in the past")
|
|
end
|
|
end
|
|
|
|
def discount_cannot_be_greater_than_total_value
|
|
if discount > total_value
|
|
errors.add(:discount, "can't be greater than total value")
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
By default such validations will run every time you call `valid?`. It is also
|
|
possible to control when to run these custom validations by giving an `:on`
|
|
option to the `validate` method, with either: `:create` or `:update`.
|
|
|
|
```ruby
|
|
class Invoice < ActiveRecord::Base
|
|
validate :active_customer, on: :create
|
|
|
|
def active_customer
|
|
errors.add(:customer_id, "is not active") unless customer.active?
|
|
end
|
|
end
|
|
```
|
|
|
|
Displaying Validation Errors in Views
|
|
-------------------------------------
|
|
|
|
Once you've created a model and added validations, if that model is created via
|
|
a web form, you probably want to display an error message when one of the
|
|
validations fail.
|
|
|
|
Because every application handles this kind of thing differently, Rails does
|
|
not include any view helpers to help you generate these messages directly.
|
|
However, due to the rich number of methods Rails gives you to interact with
|
|
validations in general, it's fairly easy to build your own. In addition, when
|
|
generating a scaffold, Rails will put some ERB into the `_form.html.erb` that
|
|
it generates that displays the full list of errors on that model.
|
|
|
|
Assuming we have a model that's been saved in an instance variable named
|
|
`@post`, it looks like this:
|
|
|
|
```ruby
|
|
<% if @post.errors.any? %>
|
|
<div id="error_explanation">
|
|
<h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
|
|
|
|
<ul>
|
|
<% @post.errors.full_messages.each do |msg| %>
|
|
<li><%= msg %></li>
|
|
<% end %>
|
|
</ul>
|
|
</div>
|
|
<% end %>
|
|
```
|
|
|
|
Furthermore, if you use the Rails form helpers to generate your forms, when
|
|
a validation error occurs on a field, it will generate an extra `<div>` around
|
|
the entry.
|
|
|
|
```
|
|
<div class="field_with_errors">
|
|
<input id="post_title" name="post[title]" size="30" type="text" value="">
|
|
</div>
|
|
```
|
|
|
|
You can then style this div however you'd like. The default scaffold that
|
|
Rails generates, for example, adds this CSS rule:
|
|
|
|
```
|
|
.field_with_errors {
|
|
padding: 2px;
|
|
background-color: red;
|
|
display: table;
|
|
}
|
|
```
|
|
|
|
This means that any field with an error ends up with a 2 pixel red border.
|