Active Record Validations
This guide teaches you how to validate Active Record objects before saving themto the database using Active Record's validations feature.
After reading this guide, you will know:
- How to use the built-in Active Record validations and options.
- How to check the validity of objects.
- How to create conditional and strict validations.
- How to create your own custom validation methods.
- How to work with the validation error messages and displaying them in views.
1. Validations Overview
Here's an example of a very simple validation:
classPerson<ApplicationRecordvalidates:name,presence:trueendirb>Person.new(name:"John Doe").valid?=>trueirb>Person.new(name:nil).valid?=>falseAs you can see, thePerson is not valid without aname attribute.
Before we dig into more details, let's talk about how validations fit into thebig picture of your application.
1.1. 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 userprovides a valid email address and mailing address. Model-level validations arethe best way to ensure that only valid data is saved into your database. Theycan be used with any database, cannot be bypassed by end users, and areconvenient to test and maintain. Rails provides built-in helpers for commonneeds, and allows you to create your own validation methods as well.
1.2. Alternate Ways to Validate
There are several other ways to validate data before it is saved into yourdatabase, including native database constraints, client-side validations andcontroller-level validations. Here's a summary of the pros and cons:
- Database constraints and/or stored procedures make the validation mechanismsdatabase-dependent and can make testing and maintenance more difficult.However, if your database is used by other applications, it may be a good ideato use some constraints at the database level. Additionally, database-levelvalidations can safely handle some things (such as uniqueness in heavily-usedtables) that can be difficult to implement otherwise.
- Client-side validations can be useful, but are generally unreliable if usedalone. If they are implemented using JavaScript, they may be bypassed ifJavaScript is turned off in the user's browser. However, if combined withother techniques, client-side validation can be a convenient way to provideusers with immediate feedback as they use your site.
- Controller-level validations can be tempting to use, but often become unwieldyand difficult to test and maintain. Whenever possible, it's a good idea tokeep your controllers simple, as it will make working with your applicationeasier in the long run.
Rails recommends using model-level validations in most circumstances, howeverthere may be specific cases where you want to complement them with alternatevalidations.
1.3. Validation Triggers
There are two kinds of Active Record objects - those that correspond to a rowinside your database and those that do not. When you instantiate a new object,using thenew method, the object does not get saved in the database as yet.Once you callsave on that object, it will be saved into the appropriatedatabase table. Active Record uses an instance method calledpersisted? (andits inversenew_record?) to determine whether an object is already in thedatabase or not. Consider the following Active Record class:
classPerson<ApplicationRecordendWe can see how it works by looking at somebin/rails console output:
irb>p=Person.new(name:"Jane Doe")=>#<Personid:nil,name:"Jane Doe",created_at:nil,updated_at:nil>irb>p.new_record?=>trueirb>p.persisted?=>falseirb>p.save=>trueirb>p.new_record?=>falseirb>p.persisted?=>trueSaving a new record will send an SQLINSERT operation to the database, whereasupdating an existing record will send an SQLUPDATE operation. Validations aretypically run before these commands are sent to the database. If any validationsfail, the object will be marked as invalid and Active Record will not performtheINSERT orUPDATE operation. This helps to avoid storing an invalidobject in the database. You can choose to have specific validations run when anobject is created, saved, or updated.
While validations usually prevent invalid data from being saved to thedatabase, it's important to be aware that not all methods in Rails triggervalidations. Some methods allow changes to be made directly to the databasewithout performing validations. As a result, if you're not careful, it’spossible tobypass validations and save an object in aninvalid state.
The following methods trigger validations, and will save the object to thedatabase only if the object is valid:
The bang versions (methods that end with an exclamation mark, likesave!)raise an exception if the record is invalid. The non-bang versions -save andupdate returnsfalse, andcreate returns the object.
1.4. Skipping Validations
The following methods skip validations, and will save the object to the databaseregardless of its validity. They should be used with caution. Refer to themethod documentation to learn more.
decrement!decrement_counterincrement!increment_counterinsertinsert!insert_allinsert_all!toggle!touchtouch_allupdate_allupdate_attributeupdate_attribute!update_columnupdate_columnsupdate_countersupsertupsert_allsave(validate: false)
save also has the ability to skip validations ifvalidate: false ispassed as an argument. This technique should be used with caution.
1.5. Checking Validity
Before saving an Active Record object, Rails runs your validations, and if thesevalidations produce any validation errors, then Rails will not save the object.
You can also run the validations on your own.valid? triggers yourvalidations and returns true if no errors are found in the object, and falseotherwise. As you saw above:
classPerson<ApplicationRecordvalidates:name,presence:trueendirb>Person.new(name:"John Doe").valid?=>trueirb>Person.new(name:nil).valid?=>falseAfter Active Record has performed validations, any failures can be accessedthrough theerrors instance method, which returns a collection of errors.By definition, an object is valid if the collection is empty after runningvalidations.
An object instantiated withnew will not report errors even if it'stechnically invalid, because validations are automatically run only when theobject is saved, such as with thecreate orsave methods.
classPerson<ApplicationRecordvalidates:name,presence:trueendirb>person=Person.new=>#<Personid:nil,name:nil,created_at:nil,updated_at:nil>irb>person.errors.size=>0irb>person.valid?=>falseirb>person.errors.objects.first.full_message=>"Name can't be blank"irb>person.save=>falseirb>person.save!ActiveRecord::RecordInvalid: Validation failed: Name can't be blankirb>Person.create!ActiveRecord::RecordInvalid: Validation failed: Name can't be blankinvalid? is the inverse ofvalid?. It triggers your validations,returning true if any errors were found in the object, and false otherwise.
1.6. Inspecting and Handling Errors
To verify whether or not a particular attribute of an object is valid, you canuseerrors[:attribute]. It returns an array of allthe error messages for:attribute. If there are no errors on the specifiedattribute, an empty array is returned. This allows you to easily determinewhether there are any validation issues with a specific attribute.
Here’s an example illustrating how to check for errors on an attribute:
classPerson<ApplicationRecordvalidates:name,presence:trueendirb>new_person=Person.newirb>new_person.errors[:name]=>[]# no errors since validations are not run until savedirb>new_person.errors[:name].any?=>falseirb>create_person=Person.createirb>create_person.errors[:name]=>["can't be blank"]# validation error because `name` is requiredirb>create_person.errors[:name].any?=>trueAdditionally, you can use theerrors.addmethod to manually add error messages for specific attributes. This isparticularly useful when defining custom validation scenarios.
classPerson<ApplicationRecordvalidatedo|person|errors.add:name,:too_short,message:"is not long enough"endendTo read about validation errors in greater depth refer to theWorkingwith Validation Errors section.
2. Validations
Active Record offers many predefined validations that you can use directlyinside your class definitions. These predefined validations provide commonvalidation rules. Each time a validation fails, an error message is added to theobject'serrors collection, and this error is associated with the specificattribute being validated.
When a validation fails, the error message is stored in theerrors collectionunder the attribute name that triggered the validation. This means you caneasily access the errors related to any specific attribute. For instance, if youvalidate the:name attribute and the validation fails, you will find the errormessage undererrors[:name].
In modern Rails applications, the more concise validate syntax is commonly used,for example:
validates:name,presence:trueHowever, older versions of Rails used "helper" methods, such as:
validates_presence_of:nameBoth notations perform the same function, but the newer form is recommended forits readability and alignment with Rails' conventions.
Each validation accepts an arbitrary number of attribute names, allowing you toapply the same type of validation to multiple attributes in a single line ofcode.
Additionally, all validations accept the:on and:message options. The:onoption specifies when the validation should be triggered, with possible valuesbeing:create or:update. The:message option allows you to define acustom error message that will be added to the errors collection if thevalidation fails. If you do not specify a message, Rails will use a defaulterror message for that validation.
To see a list of the available default helpers, take a look atActiveModel::Validations::HelperMethods. This API section uses the oldernotation as described above.
Below we outline the most commonly used validations.
2.1.absence
This validator validates that the specified attributes are absent. It uses theObject#present? method to check if the value is neither nil nor a blankstring - that is, a string that is either empty or consists of whitespace only.
#absence is commonly used for conditional validations. For example:
classPerson<ApplicationRecordvalidates:phone_number,:address,absence:true,if: :invited?endirb>person=Person.new(name:"Jane Doe",invitation_sent_at:Time.current)irb>person.valid?=>true# absence validation passesIf you want to be sure that an association is absent, you'll need to testwhether the associated object itself is absent, and not the foreign key used tomap the association.
classLineItem<ApplicationRecordbelongs_to:order,optional:truevalidates:order,absence:trueendirb>line_item=LineItem.newirb>line_item.valid?=>true# absence validation passesorder=Order.createirb>line_item_with_order=LineItem.new(order:order)irb>line_item_with_order.valid?=>false# absence validation failsForbelongs_to the association presence is validated by default. If youdon’t want to have association presence validated, useoptional: true.
Rails will usually infer the inverse association automatically. In cases whereyou use a custom:foreign_key or a:through association, it's important toexplicitly set the:inverse_of option to optimize the association lookup. Thishelps avoid unnecessary database queries during validation.
For more details, check out theBi-directional Associationsdocumentation.
If you want to ensure that the association is both present and valid, youalso need to usevalidates_associated. More on that in thevalidates_associated section.
If you validate the absence of an object associated via ahas_one orhas_many relationship, itwill check that the object is neitherpresent? normarked_for_destruction?.
Sincefalse.present? is false, if you want to validate the absence of aboolean field you should use:
validates:field_name,exclusion:{in:[true,false]}The default error message is"must be blank".
2.2.acceptance
This method validates that a checkbox on the user interface was checked when aform was submitted. This is typically used when the user needs to agree to yourapplication's terms of service, confirm that some text is read, or any similarconcept.
classPerson<ApplicationRecordvalidates:terms_of_service,acceptance:trueendThis check is performed only ifterms_of_service is notnil. The defaulterror message for this validation is"must be accepted". You can also pass ina custom message via themessage option.
classPerson<ApplicationRecordvalidates:terms_of_service,acceptance:{message:"must be agreed to"}endIt can also receive an:accept option, which determines the allowed valuesthat will be considered as acceptable. It defaults to['1', true] and can beeasily changed.
classPerson<ApplicationRecordvalidates:terms_of_service,acceptance:{accept:"yes"}validates:eula,acceptance:{accept:["TRUE","accepted"]}endThis validation is very specific to web applications and this 'acceptance' doesnot need to be recorded anywhere in your database. If you don't have a field forit, the validator will create a virtual attribute. If the field does exist inyour database, theaccept option must be set to or includetrue or else thevalidation will not run.
2.3.confirmation
You should use this validator when you have two text fields that should receiveexactly the same content. For example, you may want to confirm an email addressor a password. This validation creates a virtual attribute whose name is thename of the field that has to be confirmed with "_confirmation" appended.
classPerson<ApplicationRecordvalidates:email,confirmation:trueendIn your view template you could use something like
<%=text_field:person,:email%><%=text_field:person,:email_confirmation%>This check is performed only ifemail_confirmation is notnil. Torequire confirmation, make sure to add a presence check for the confirmationattribute (we'll take a look at thepresence check later on inthis guide):
classPerson<ApplicationRecordvalidates:email,confirmation:truevalidates:email_confirmation,presence:trueendThere is also a:case_sensitive option that you can use to define whether theconfirmation constraint will be case sensitive or not. This option defaults totrue.
classPerson<ApplicationRecordvalidates:email,confirmation:{case_sensitive:false}endThe default error message for this validator is"doesn't match confirmation".You can also pass in a custom message via themessage option.
Generally when using this validator, you will want to combine it with the:ifoption to only validate the "_confirmation" field when the initial field haschanged andnot every time you save the record. More onconditionalvalidations later.
classPerson<ApplicationRecordvalidates:email,confirmation:truevalidates:email_confirmation,presence:true,if: :email_changed?end2.4.comparison
This validator will validate a comparison between any two comparable values.
classPromotion<ApplicationRecordvalidates:end_date,comparison:{greater_than: :start_date}endThe default error message for this validator is"failed comparison". You canalso pass in a custom message via themessage option.
These options are all supported:
| Option | Description | Default Error Message |
|---|---|---|
:greater_than | Specifies the value must be greater than the supplied value. | "must be greater than %{count}" |
:greater_than_or_equal_to | Specifies the value must be greater than or equal to the supplied value. | "must be greater than or equal to %{count}" |
:equal_to | Specifies the value must be equal to the supplied value. | "must be equal to %{count}" |
:less_than | Specifies the value must be less than the supplied value. | "must be less than %{count}" |
:less_than_or_equal_to | Specifies the value must be less than or equal to the supplied value. | "must be less than or equal to %{count}" |
:other_than | Specifies the value must be other than the supplied value. | "must be other than %{count}" |
The validator requires a compare option be supplied. Each option accepts avalue, proc, or symbol. Any class that includesComparable can be compared.
2.5.format
This validator validates the attributes' values by testing whether they match agiven regular expression, which is specified using the:with option.
classProduct<ApplicationRecordvalidates:legacy_code,format:{with:/\A[a-zA-Z]+\z/,message:"only allows letters"}endInversely, by using the:without option instead you can require that thespecified attribute doesnot match the regular expression.
In either case, the provided:with or:without option must be a regularexpression or a proc or lambda that returns one.
The default error message is"is invalid".
Use\A and\z to match the start and end of the string,^ and$match the start/end of a line. Due to frequent misuse of^ and$, you needto pass themultiline: true option in case you use any of these two anchors inthe provided regular expression. In most cases, you should be using\A and\z.
2.6.inclusion andexclusion
Both of these validators validate whether an attribute’s value is included orexcluded from a given set. The set can be any enumerable object such as anarray, range, or a dynamically generated collection using a proc, lambda, orsymbol.
inclusionensures that the value is present in the set.exclusionensures that the value isnot present in the set.
In both cases, the option:in receives the set of values, and:within can beused as an alias. For full options on customizing error messages, see themessage documentation.
If the enumerable is a numerical, time, or datetime range, the test is performedusingRange#cover?, otherwise, it usesinclude?. When using a proc orlambda, the instance under validation is passed as an argument, allowing fordynamic validation.
2.6.1. Examples
Forinclusion:
classCoffee<ApplicationRecordvalidates:size,inclusion:{in:%w(small medium large),message:"%{value} is not a valid size"}endForexclusion:
classAccount<ApplicationRecordvalidates:subdomain,exclusion:{in:%w(www us ca jp),message:"%{value} is reserved."}endBoth validators allow the use of dynamic validation through methods that returnan enumerable. Here’s an example using a proc forinclusion:
classCoffee<ApplicationRecordvalidates:size,inclusion:{in:->(coffee){coffee.available_sizes}}defavailable_sizes%w(small medium large extra_large)endendSimilarly, forexclusion:
classAccount<ApplicationRecordvalidates:subdomain,exclusion:{in:->(account){account.reserved_subdomains}}defreserved_subdomains%w(www us ca jp admin)endend2.7.length
This validator validates the length of the attributes' values. It provides avariety of options, so you can specify length constraints in different ways:
classPerson<ApplicationRecordvalidates:name,length:{minimum:2}validates:bio,length:{maximum:500}validates:password,length:{in:6..20}validates:registration_number,length:{is:6}endThe possible length constraint options are:
| Option | Description |
|---|---|
:minimum | The attribute cannot have less than the specified length. |
:maximum | The attribute cannot have more than the specified length. |
:in | 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 beingperformed. You can customize these messages using the:wrong_length,:too_long, and:too_short options and%{count} as a placeholder for thenumber corresponding to the length constraint being used. You can still use the:message option to specify an error message.
classPerson<ApplicationRecordvalidates:bio,length:{maximum:1000,too_long:"%{count} characters is the maximum allowed"}endThe default error messages are plural (e.g. "is too short (minimum is%{count} characters)"). For this reason, when:minimum is 1 you should providea custom message or usepresence: true instead. Similarly, when:in or:within have a lower limit of 1, you should either provide a custom message orcallpresence prior tolength. Only one constraint option can be used at atime apart from the:minimum and:maximum options which can be combinedtogether.
2.8.numericality
This validator validates that your attributes have only numeric values. Bydefault, it will match an optional sign followed by an integer or floating pointnumber.
To specify that only integer numbers are allowed, set:only_integer to true.Then it will use the following regular expression to validate the attribute'svalue.
/\A[+-]?\d+\z/Otherwise, it will try to convert the value to a number usingFloat.Floatsare converted toBigDecimal using the column's precision value or a maximum of15 digits.
classPlayer<ApplicationRecordvalidates:points,numericality:truevalidates:games_played,numericality:{only_integer:true}endThe default error message for:only_integer is"must be an integer".
Besides:only_integer, this validator also accepts the:only_numeric optionwhich specifies the value must be an instance ofNumeric and attempts to parsethe value if it is aString.
By default,numericality doesn't allownil values. You can useallow_nil: true option to permit it. ForInteger andFloat columns emptystrings are converted tonil.
The default error message when no options are specified is"is not a number".
There are also many options that can be used to add constraints to acceptablevalues:
| Option | Description | Default Error Message |
|---|---|---|
:greater_than | Specifies the value must be greater than the supplied value. | "must be greater than %{count}" |
:greater_than_or_equal_to | Specifies the value must be greater than or equal to the supplied value. | "must be greater than or equal to %{count}" |
:equal_to | Specifies the value must be equal to the supplied value. | "must be equal to %{count}" |
:less_than | Specifies the value must be less than the supplied value. | "must be less than %{count}" |
:less_than_or_equal_to | Specifies the value must be less than or equal to the supplied value. | "must be less than or equal to %{count}" |
:other_than | Specifies the value must be other than the supplied value. | "must be other than %{count}" |
:in | Specifies the value must be in the supplied range. | "must be in %{count}" |
:odd | Specifies the value must be an odd number. | "must be odd" |
:even | Specifies the value must be an even number. | "must be even" |
2.9.presence
This validator validates that the specified attributes are not empty. It usestheObject#blank? method to check if the value is eithernil or a blankstring - that is, a string that is either empty or consists of whitespace.
classPerson<ApplicationRecordvalidates:name,:login,:email,presence:trueendperson = Person.new(name: "Alice", login: "alice123", email: "alice@example.com")person.valid?=>true# presence validation passesinvalid_person=Person.new(name:"",login:nil,email:"bob@example.com")invalid_person.valid?=>false# presence validation failsTo check that an association is present, you'll need to test that the associatedobject is present, and not the foreign key used to map the association. Testingthe association will help you to determine that the foreign key is not empty andalso that the referenced object exists.
classSupplier<ApplicationRecordhas_one:accountvalidates:account,presence:trueendirb>account=Account.create(name:"Account A")irb>supplier=Supplier.new(account:account)irb>supplier.valid?=>true# presence validation passesirb>invalid_supplier=Supplier.newirb>invalid_supplier.valid?=>false# presence validation failsIn cases where you use a custom:foreign_key or a:through association, it'simportant to explicitly set the:inverse_of option to optimize the associationlookup. This helps avoid unnecessary database queries during validation.
For more details, check out theBi-directional Associationsdocumentation.
If you want to ensure that the association is both present and valid, youalso need to usevalidates_associated. More on thatbelow.
If you validate the presence of an object associated via ahas_one orhas_many relationship, itwill check that the object is neitherblank? normarked_for_destruction?.
Sincefalse.blank? is true, if you want to validate the presence of a booleanfield you should use one of the following validations:
# Value _must_ be true or falsevalidates:boolean_field_name,inclusion:[true,false]# Value _must not_ be nil, aka true or falsevalidates:boolean_field_name,exclusion:[nil]By using one of these validations, you will ensure the value will NOT benilwhich would result in aNULL value in most cases.
The default error message is"can't be blank".
2.10.uniqueness
This validator validates that the attribute's value is unique right before theobject gets saved.
classAccount<ApplicationRecordvalidates:email,uniqueness:trueendThe 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 one or more attributesthat are used to limit the uniqueness check:
classHoliday<ApplicationRecordvalidates:name,uniqueness:{scope: :year,message:"should happen once per year"}endThis validation does not create a uniqueness constraint in thedatabase, so a scenario can occur whereby two different database connectionscreate two records with the same value for a column that you intended to beunique. To avoid this, you must create a unique index on that column in yourdatabase.
In order to add a uniqueness database constraint on your database, use theadd_index statement in a migration and include theunique: true option.
If you are using the:scope option in your uniqueness validation, and you wishto create a database constraint to prevent possible violations of the uniquenessvalidation, you must create a unique index on both columns in your database. Seethe MySQL manual andthe MariaDB manual for more details about multiplecolumn indexes, orthe PostgreSQL manual for examples of unique constraintsthat refer to a group of columns.
There is also a:case_sensitive option that you can use to define whether theuniqueness constraint will be case sensitive, case insensitive, or if it shouldrespect the default database collation. This option defaults to respecting thedefault database collation.
classPerson<ApplicationRecordvalidates:name,uniqueness:{case_sensitive:false}endSome databases are configured to perform case-insensitive searchesanyway.
A:conditions option can be used to specify additional conditions as aWHERESQL fragment to limit the uniqueness constraint lookup:
validates:name,uniqueness:{conditions:->{where(status:"active")}}The default error message is"has already been taken".
Seevalidates_uniqueness_of for more information.
2.11.validates_associated
You should use this validator when your model has associations that always needto be validated. Every time you try to save your object,valid? will be calledon each one of the associated objects.
classLibrary<ApplicationRecordhas_many:booksvalidates_associated:booksendThis validation will work with all of the association types.
Don't usevalidates_associated on both ends of your associations.They would call each other in an infinite loop.
The default error message forvalidates_associated is"is invalid". Notethat each associated object will contain its ownerrors collection; errors donot bubble up to the calling model.
validates_associated can only be used with ActiveRecord objects,everything up until now can also be used on any object which includesActiveModel::Validations.
2.12.validates_each
This validator validates attributes against a block. It doesn't have apredefined validation function. You should create one using a block, and everyattribute passed tovalidates_each will be tested against it.
In the following example, we will reject names and surnames that begin withlowercase.
classPerson<ApplicationRecordvalidates_each:name,:surnamedo|record,attr,value|record.errors.add(attr,"must start with upper case")if/\A[[:lower:]]/.match?(value)endendThe 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 yourvalidation fails, you should add an error to the model, therefore making itinvalid.
2.13.validates_with
This validator passes the record to a separate class for validation.
classAddressValidator<ActiveModel::Validatordefvalidate(record)ifrecord.house_number.blank?record.errors.add:house_number,"is required"endifrecord.street.blank?record.errors.add:street,"is required"endifrecord.postcode.blank?record.errors.add:postcode,"is required"endendendclassInvoice<ApplicationRecordvalidates_withAddressValidatorendThere is no default error message forvalidates_with. You must manually adderrors to the record's errors collection in the validator class.
Errors added torecord.errors[:base] relate to the state of the recordas a whole.
To implement the validate method, you must accept arecord parameter in themethod definition, which is the record to be validated.
If you want to add an error on a specific attribute, you can pass it as thefirst argument to theadd method.
defvalidate(record)ifrecord.some_field!="acceptable"record.errors.add:some_field,"this field is unacceptable"endendWe will covervalidation errors in greaterdetail later.
Thevalidates_with validator takes a class, or a list of classes to usefor validation.
classPerson<ApplicationRecordvalidates_withMyValidator,MyOtherValidator,on: :createendLike all other validations,validates_with takes the:if,:unless and:on options. If you pass any other options, it will send those options to thevalidator class asoptions:
classAddressValidator<ActiveModel::Validatordefvalidate(record)options[:fields].eachdo|field|ifrecord.send(field).blank?record.errors.addfield,"is required"endendendendclassInvoice<ApplicationRecordvalidates_withAddressValidator,fields:[:house_number,:street,:postcode,:country]endThe validator will be initializedonly once for the whole applicationlife cycle, and not on each validation run, so be careful about using instancevariables inside it.
If your validator is complex enough that you want instance variables, you caneasily use a plain old Ruby object instead:
classInvoice<ApplicationRecordvalidatedo|invoice|AddressValidator.new(invoice).validateendendclassAddressValidatordefinitialize(invoice)@invoice=invoiceenddefvalidatevalidate_field(:house_number)validate_field(:street)validate_field(:postcode)endprivatedefvalidate_field(field)if@invoice.send(field).blank?@invoice.errors.addfield,"#{field.to_s.humanize} is required"endendendWe will covercustom validations more later.
3. Validation Options
There are several common options supported by the validators. These options are:
:allow_nil: Skip validation if the attribute isnil.:allow_blank: Skip validation if the attribute is blank.:message: Specify a custom error message.:on: Specify the contexts where this validation is active.:strict: Raise an exception when the validationfails.:ifand:unless: Specify when the validationshould or should not occur.
Not all of these options are supported by every validator, please refer tothe API documentation forActiveModel::Validations.
3.1.:allow_nil
The:allow_nil option skips the validation when the value being validated isnil.
classCoffee<ApplicationRecordvalidates:size,inclusion:{in:%w(small medium large),message:"%{value} is not a valid size"},allow_nil:trueendirb>Coffee.create(size:nil).valid?=>trueirb>Coffee.create(size:"mega").valid?=>falseFor full options to the message argument please see themessagedocumentation.
3.2.:allow_blank
The:allow_blank option is similar to the:allow_nil option. This optionwill let validation pass if the attribute's value isblank?, likenil or anempty string for example.
classTopic<ApplicationRecordvalidates:title,length:{is:6},allow_blank:trueendirb>Topic.create(title:"").valid?=>trueirb>Topic.create(title:nil).valid?=>trueirb>Topic.create(title:"short").valid?=>false# 'short' is not of length 6, so validation fails even though it's not blank3.3.:message
As you've already seen, the:message option lets you specify the message thatwill be added to theerrors collection when validation fails. When this optionis not used, Active Record will use the respective default error message foreach validation.
The:message option accepts either aString orProc as its value.
AString:message value can optionally contain any/all of%{value},%{attribute}, and%{model} which will be dynamically replaced whenvalidation fails. This replacement is done using thei18ngem, and the placeholders must matchexactly, no spaces are allowed.
classPerson<ApplicationRecord# Hard-coded messagevalidates:name,presence:{message:"must be given please"}# Message with dynamic attribute value. %{value} will be replaced# with the actual value of the attribute. %{attribute} and %{model}# are also available.validates:age,numericality:{message:"%{value} seems wrong"}endAProc:message value is given two arguments: the object being validated,and a hash with:model,:attribute, and:value key-value pairs.
classPerson<ApplicationRecordvalidates:username,uniqueness:{# object = person object being validated# data = { model: "Person", attribute: "Username", value: <username> }message:->(object,data)do"Hey#{object.name},#{data[:value]} is already taken."end}endTo translate error messages, see theI18nguide.
3.4.:on
The:on option lets you specify when the validation should happen. The defaultbehavior for all the built-in validations is to be run on save (both when you'recreating a new record and when you're updating it). If you want to change it,you can useon: :create to run the validation only when a new record iscreated oron: :update to run the validation only when a record is updated.
classPerson<ApplicationRecord# it will be possible to update email with a duplicated valuevalidates:email,uniqueness:true,on: :create# it will be possible to create the record with a non-numerical agevalidates:age,numericality:true,on: :update# the default (validates on both create and update)validates:name,presence:trueendYou can also use:on to define custom contexts. Custom contexts need to betriggered explicitly by passing the name of the context tovalid?,invalid?,orsave.
classPerson<ApplicationRecordvalidates:email,uniqueness:true,on: :account_setupvalidates:age,numericality:true,on: :account_setupendirb>person=Person.new(age:'thirty-three')irb>person.valid?=>trueirb>person.valid?(:account_setup)=>falseirb>person.errors.messages=>{:email=>["has already been taken"],:age=>["is not a number"]}person.valid?(:account_setup) executes both the validations without saving themodel.person.save(context: :account_setup) validatesperson in theaccount_setup context before saving.
Passing an array of symbols is also acceptable.
classBookincludeActiveModel::Validationsvalidates:title,presence:true,on:[:update,:ensure_title]endirb>book=Book.new(title:nil)irb>book.valid?=>trueirb>book.valid?(:ensure_title)=>falseirb>book.errors.messages=>{:title=>["can't be blank"]}When triggered by an explicit context, validations are run for that context, aswell as any validationswithout a context.
classPerson<ApplicationRecordvalidates:email,uniqueness:true,on: :account_setupvalidates:age,numericality:true,on: :account_setupvalidates:name,presence:trueendirb>person=Person.newirb>person.valid?(:account_setup)=>falseirb>person.errors.messages=>{:email=>["has already been taken"],:age=>["is not a number"],:name=>["can't be blank"]}You can read more about use-cases for:on in theCustom Contextssection.
4. Conditional Validations
Sometimes it will make sense to validate an object only when a given conditionis met. You can do that by using the:if and:unless options, which can takea symbol, aProc or anArray. You may use the:if option when you want tospecify when the validationshould happen. Alternatively, if you want tospecify when the validationshould not happen, then you may use the:unless option.
4.1. Using a Symbol with:if and:unless
You can associate the:if and:unless options with a symbol corresponding tothe name of a method that will get called right before validation happens. Thisis the most commonly used option.
classOrder<ApplicationRecordvalidates:card_number,presence:true,if: :paid_with_card?defpaid_with_card?payment_type=="card"endend4.2. Using a Proc with:if and:unless
It is possible to associate:if and:unless with aProc object which willbe called. Using aProc object gives you the ability to write an inlinecondition instead of a separate method. This option is best suited forone-liners.
classAccount<ApplicationRecordvalidates:password,confirmation:true,unless:Proc.new{|a|a.password.blank?}endAslambda is a type ofProc, it can also be used to write inline conditionstaking advantage of the shortened syntax.
validates:password,confirmation:true,unless:->{password.blank?}4.3. Grouping Conditional Validations
Sometimes it is useful to have multiple validations use one condition. It can beeasily achieved usingwith_options.
classUser<ApplicationRecordwith_optionsif: :is_admin?do|admin|admin.validates:password,length:{minimum:10}admin.validates:email,presence:trueendendAll validations inside of thewith_options block will automatically haveif::is_admin? merged into its options.
4.4. Combining Validation Conditions
On the other hand, when multiple conditions define whether or not a validationshould happen, anArray can be used. Moreover, you can apply both:if and:unless to the same validation.
classComputer<ApplicationRecordvalidates:mouse,presence:true,if:[Proc.new{|c|c.market.retail?},:desktop?],unless:Proc.new{|c|c.trackpad.present?}endThe validation only runs when all the:if conditions and none of the:unlessconditions are evaluated totrue.
5. Strict Validations
You can also specify validations to be strict and raiseActiveModel::StrictValidationFailed when the object is invalid.
classPerson<ApplicationRecordvalidates:name,presence:{strict:true}endirb>Person.new.valid?=>ActiveModel::StrictValidationFailed:Namecan'tbeblankStrict validations ensure that an exception is raised immediately whenvalidation fails, which can be useful in situations where you want to enforceimmediate feedback or halt processing when invalid data is encountered. Forexample, you might use strict validations in a scenario where invalid inputshould prevent further operations, such as when processing critical transactionsor performing data integrity checks.
There is also the ability to pass a custom exception to the:strict option.
classPerson<ApplicationRecordvalidates:token,presence:true,uniqueness:true,strict:TokenGenerationExceptionendirb>Person.new.valid?=>TokenGenerationException:Tokencan'tbeblank6. Listing Validators
If you want to find out all of the validators for a given object, you can usevalidators.
For example, if we have the following model using a custom validator and abuilt-in validator:
classPerson<ApplicationRecordvalidates:name,presence:true,on: :createvalidates:email,format:URI::MailTo::EMAIL_REGEXPvalidates_withMyOtherValidator,strict:trueendWe can now usevalidators on the "Person" model to list all validators, oreven check a specific field usingvalidators_on.
irb>Person.validators#=> [#<ActiveRecord::Validations::PresenceValidator:0x10b2f2158 @attributes=[:name], @options={:on=>:create}>, #<MyOtherValidatorValidator:0x10b2f17d0 @attributes=[:name], @options={:strict=>true}>, #<ActiveModel::Validations::FormatValidator:0x10b2f0f10 @attributes=[:email], @options={:with=>URI::MailTo::EMAIL_REGEXP}>] #<MyOtherValidator:0x10b2f0948 @options={:strict=>true}>]irb>Person.validators_on(:name)#=> [#<ActiveModel::Validations::PresenceValidator:0x10b2f2158 @attributes=[:name], @options={on: :create}>]7. Performing Custom Validations
When the built-in validations are not enough for your needs, you can write yourown validators or validation methods as you prefer.
7.1. Custom Validators
Custom validators are classes that inherit fromActiveModel::Validator.These classes must implement thevalidate method which takes a record as anargument and performs the validation on it. The custom validator is called usingthevalidates_with method.
classMyValidator<ActiveModel::Validatordefvalidate(record)unlessrecord.name.start_with?"X"record.errors.add:name,"Provide a name starting with X, please!"endendendclassPerson<ApplicationRecordvalidates_withMyValidatorendThe easiest way to add custom validators for validating individual attributes iswith the convenientActiveModel::EachValidator. In this case, the customvalidator class must implement avalidate_each method which takes threearguments: record, attribute, and value. These correspond to the instance, theattribute to be validated, and the value of the attribute in the passedinstance.
classEmailValidator<ActiveModel::EachValidatordefvalidate_each(record,attribute,value)unlessURI::MailTo::EMAIL_REGEXP.match?(value)record.errors.addattribute,(options[:message]||"is not an email")endendendclassPerson<ApplicationRecordvalidates:email,presence:true,email:trueendAs shown in the example, you can also combine standard validations with your owncustom validators.
7.2. Custom Methods
You can also create methods that verify the state of your models and add errorsto theerrors collection when they are invalid. You must then register thesemethods by using thevalidate class method, passing in the symbols for thevalidation methods' names.
You can pass more than one symbol for each class method and the respectivevalidations will be run in the same order as they were registered.
Thevalid? method will verify that theerrors collection is empty, so yourcustom validation methods should add errors to it when you wish validation tofail:
classInvoice<ApplicationRecordvalidate:expiration_date_cannot_be_in_the_past,:discount_cannot_be_greater_than_total_valuedefexpiration_date_cannot_be_in_the_pastifexpiration_date.present?&&expiration_date<Date.todayerrors.add(:expiration_date,"can't be in the past")endenddefdiscount_cannot_be_greater_than_total_valueifdiscount>total_valueerrors.add(:discount,"can't be greater than total value")endendendBy default, such validations will run every time you callvalid? or save theobject. But it is also possible to control when to run these custom validationsby giving an:on option to thevalidate method, with either::create or:update.
classInvoice<ApplicationRecordvalidate:active_customer,on: :createdefactive_customererrors.add(:customer_id,"is not active")unlesscustomer.active?endendSee the section above for more details about:on.
7.3. Custom Contexts
You can define your own custom validation contexts for callbacks, which isuseful when you want to perform validations based on specific scenarios or groupcertain callbacks together and run them in a specific context. A common scenariofor custom contexts is when you have a multi-step form and want to performvalidations per step.
For instance, you might define custom contexts for each step of the form:
classUser<ApplicationRecordvalidate:personal_information,on: :personal_infovalidate:contact_information,on: :contact_infovalidate:location_information,on: :location_infoprivatedefpersonal_informationerrors.add(:base,"Name must be present")iffirst_name.blank?errors.add(:base,"Age must be at least 18")ifage&&age<18enddefcontact_informationerrors.add(:base,"Email must be present")ifemail.blank?errors.add(:base,"Phone number must be present")ifphone.blank?enddeflocation_informationerrors.add(:base,"Address must be present")ifaddress.blank?errors.add(:base,"City must be present")ifcity.blank?endendIn these cases, you may be tempted toskipcallbacks altogether, butdefining a custom context can be a more structured approach. You will need tocombine a context with the:on option to define a custom context for acallback.
Once you've defined the custom context, you can use it to trigger thevalidations:
irb>user=User.new(name:"John Doe",age:17,email:"jane@example.com",phone:"1234567890",address:"123 Main St")irb>user.valid?(:personal_info)# => falseirb>user.valid?(:contact_info)# => trueirb>user.valid?(:location_info)# => falseYou can also use the custom contexts to trigger the validations on any methodthat supports callbacks. For example, you could use the custom context totrigger the validations onsave:
irb>user=User.new(name:"John Doe",age:17,email:"jane@example.com",phone:"1234567890",address:"123 Main St")irb>user.save(context: :personal_info)# => falseirb>user.save(context: :contact_info)# => trueirb>user.save(context: :location_info)# => false8. Working with Validation Errors
Thevalid? andinvalid? methods only provide a summary status onvalidity. However you can dig deeper into each individual error by using variousmethods from theerrors collection.
The following is a list of the most commonly used methods. Please refer to theActiveModel::Errors documentation for a list of all the available methods.
8.1.errors
Theerrors method is the starting point through which you can drill downinto various details of each error.
This returns an instance of the classActiveModel::Errors containing allerrors, each error is represented by anActiveModel::Error object.
classPerson<ApplicationRecordvalidates:name,presence:true,length:{minimum:3}endirb>person=Person.newirb>person.valid?=>falseirb>person.errors.full_messages=>["Name can't be blank","Name is too short (minimum is 3 characters)"]irb>person=Person.new(name:"John Doe")irb>person.valid?=>trueirb>person.errors.full_messages=>[]irb>person=Person.newirb>person.valid?=>falseirb>person.errors.first.details=>{:error=>:too_short,:count=>3}8.2.errors[]
errors[] is used when you want to check the errormessages for a specific attribute. It returns an array of strings with all errormessages for the given attribute, each string with one error message. If thereare no errors related to the attribute, it returns an empty array.
This method is only usefulafter validations have been run, because it onlyinspects theerrors collection and does not trigger validations itself. It'sdifferent from theActiveRecord::Base#invalid? method explained above becauseit doesn't verify the validity of the object as a whole.errors[] only checksto see whether there are errors found on an individual attribute of the object.
classPerson<ApplicationRecordvalidates:name,presence:true,length:{minimum:3}endirb>person=Person.new(name:"John Doe")irb>person.valid?=>trueirb>person.errors[:name]=>[]irb>person=Person.new(name:"JD")irb>person.valid?=>falseirb>person.errors[:name]=>["is too short (minimum is 3 characters)"]irb>person=Person.newirb>person.valid?=>falseirb>person.errors[:name]=>["can't be blank","is too short (minimum is 3 characters)"]8.3.errors.where and Error Object
Sometimes we may need more information about each error besides its message.Each error is encapsulated as anActiveModel::Error object, and thewhere method is the most common way of access.
where returns an array of error objects filtered by various degrees ofconditions.
Given the following validation:
classPerson<ApplicationRecordvalidates:name,presence:true,length:{minimum:3}endWe can filter for just theattribute by passing it as the first parameter toerrors.where(:attr). The second parameter is used for filtering thetype oferror we want by callingerrors.where(:attr, :type).
irb>person=Person.newirb>person.valid?=>falseirb>person.errors.where(:name)=>[...]# all errors for :name attributeirb>person.errors.where(:name,:too_short)=>[...]# :too_short errors for :name attributeLastly, we can filter by anyoptions that may exist on the given type of errorobject.
irb>person=Person.newirb>person.valid?=>falseirb>person.errors.where(:name,:too_short,minimum:3)=>[...]# all name errors being too short and minimum is 3You can read various information from these error objects:
irb>error=person.errors.where(:name).lastirb>error.attribute=>:nameirb>error.type=>:too_shortirb>error.options[:count]=>3You can also generate the error message:
irb>error.message=>"is too short (minimum is 3 characters)"irb>error.full_message=>"Name is too short (minimum is 3 characters)"Thefull_message method generates a more user-friendly message, with thecapitalized attribute name prepended. (To customize the format thatfull_message uses, see theI18n guide.)
8.4.errors.add
Theadd method creates the error object by taking theattribute, theerrortype and additional options hash. This is useful when writing your ownvalidator, as it lets you define very specific error situations.
classPerson<ApplicationRecordvalidatedo|person|errors.add:name,:too_plain,message:"is not cool enough"endendirb>person=Person.newirb>person.errors.where(:name).first.type=>:too_plainirb>person.errors.where(:name).first.full_message=>"Name is not cool enough"8.5.errors[:base]
You can add errors that are related to the object's state as a whole, instead ofbeing related to a specific attribute. To do this you must use:base as theattribute when adding a new error.
classPerson<ApplicationRecordvalidatedo|person|errors.add:base,:invalid,message:"This person is invalid because ..."endendirb>person=Person.newirb>person.errors.where(:base).first.full_message=>"This person is invalid because ..."8.6.errors.size
Thesize method returns the total number of errors for the object.
classPerson<ApplicationRecordvalidates:name,presence:true,length:{minimum:3}endirb>person=Person.newirb>person.valid?=>falseirb>person.errors.size=>2irb>person=Person.new(name:"Andrea",email:"andrea@example.com")irb>person.valid?=>trueirb>person.errors.size=>08.7.errors.clear
Theclear method is used when you intentionally want to clear theerrorscollection. Of course, callingerrors.clear upon an invalid object won'tactually make it valid: theerrors collection will now be empty, but the nexttime you callvalid? or any method that tries to save this object to thedatabase, the validations will run again. If any of the validations fail, theerrors collection will be filled again.
classPerson<ApplicationRecordvalidates:name,presence:true,length:{minimum:3}endirb>person=Person.newirb>person.valid?=>falseirb>person.errors.empty?=>falseirb>person.errors.clearirb>person.errors.empty?=>trueirb>person.save=>falseirb>person.errors.empty?=>false9. Displaying Validation Errors in Views
Once you've defined a model and added validations, you'll want to display anerror message when a validation fails during the creation of that model via aweb form.
Since every application handles displaying validation errors differently, Railsdoes not include any view helpers for generating these messages. However, Railsgives you a rich number of methods to interact with validations that you can useto build your own. In addition, when generating a scaffold, Rails will put somegenerated ERB into the_form.html.erb that displays the full list of errors onthat model.
Assuming we have a model that's been saved in an instance variable named@article, it looks like this:
<%if@article.errors.any?%><divid="error_explanation"><h2><%=pluralize(@article.errors.count,"error")%> prohibited this article from being saved:</h2><ul><%@article.errors.eachdo|error|%><li><%=error.full_message%></li><%end%></ul></div><%end%>Furthermore, if you use the Rails form helpers to generate your forms, when avalidation error occurs on a field, it will generate an extra<div> around theentry.
<divclass="field_with_errors"><inputid="article_title"name="article[title]"size="30"type="text"value=""></div>You can then style this div however you'd like. The default scaffold that Railsgenerates, 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.
9.1. Customizing Error Field Wrapper
Rails uses thefield_error_proc configuration option to wrap fields witherrors in HTML. By default, this option wraps the erroneous form fields in a<div> with afield_with_errors class, as seen in the example above:
config.action_view.field_error_proc=Proc.new{|html_tag,instance|content_tag:div,html_tag,class:"field_with_errors"}You can customize this behavior by modifying the field_error_proc setting inyour application configuration, allowing you to change how errors are presentedin your forms. For more details,refer to theConfiguration Guide onfield_error_proc.