Validations keep invalid data out of your database. This lesson covers Rails' built-in validations, how to test them, and how to show helpful error messages in your forms.
Important
Work on this lesson in
teachermode.
Learning goals
- You can validate records with Rails' built-in validations and know where to look up the rest.
- You can explain why a uniqueness validation, unlike other validations, needs a unique index in the database, and add both.
- You can explain when validations run, how saving behaves on an invalid record, and how to inspect the reasons.
- You can write a custom validation for a rule that spans several attributes.
- You can test a single validation in isolation, e.g. with Shoulda matchers.
- You can show validation errors next to the fields they belong to, styled so the invalid field stands out.
- You can give feedback after a form was saved or rejected, e.g. with flash messages.
Resources
Read what's new to you, skim what's familiar, skip what you already master. Stop when you can meet the learning goals.
Your agent can also generate an overview, a tutorial or an explanation for anything here, tailored to what you already know. Just ask.
- π Rails Guides:
Active Record Validations
Show archive.org snapshot
β the built-in validations,
custom validation methods
Show archive.org snapshot
,
working with
errorsShow archive.org snapshot , displaying errors in views Show archive.org snapshot and thefield_with_errorswrapper Show archive.org snapshot - π Testing ActiveRecord validations with RSpec β one spec per rule, with plain RSpec or shoulda-matchers
- π shoulda-matchers Show archive.org snapshot β setup (MovieDB doesn't ship it, add the gem) and the ActiveModel matchers reference Show archive.org snapshot
- π Rails Guides:
The Flash
Show archive.org snapshot
β
flashvs.flash.now, rendering it in the layout - π Rails Guides:
Making select boxes with ease
Show archive.org snapshot
β for the film-type
<select>in the last exercise
Exercises
Basic validations
Make the following changes to your MovieDB:
- Make sure your MovieDB models have validations for all required fields.
- Two movies must not have the same title and release year. Validate it, and back it with a unique index.
- Make sure you have specs for all your validations in MovieDB.
- Use Shoulda matchers where you think they improve the readability of your code.
Note
Most validations only look at the record being saved, so their result can't change behind your back.
uniquenessis different: it checks other rows with aSELECTbefore saving, and two requests saving at the same time can both pass that check. That's why a uniqueness validation needs a unique index in the database (add_index :movies, [:title, :release_year], unique: true). The index makes the database reject the duplicate, and the validation still shows a friendly error message in the common case.
Tip
Shoulda's examples use RSpec's one-liner syntax:
describe Movie do it { is_expected.to validate_presence_of(:title) } endThis is short for:
describe Movie do it 'validates the presence of title' do movie = Movie.new expect(movie).to validate_presence_of(:title) end end
subjectis the object under test. Fordescribe Movieit'sMovie.newby default.is_expectedis short forexpect(subject). Without a description,itgenerates one from the matcher.
Working with error messages
Understand how to use the
#errors method
Show archive.org snapshot
to inspect the reasons why a record is invalid.
Make the following changes to your MovieDB:
-
There is no longer a full list of error messages at the top of each failed form.
-
When a form could not be saved, only the fields with errors show their respective error messages.
Your forms should be triples of label, input and error message:
Title [_____________________] Title must not be blank Release year [____-5] Release year must be a positive number -
If a field has more than one error, only show the first error.
-
A web app has a lot of fields, and each must be able to display an error message. Make it easy for a form view to display the error message for a given record and field. Remove as much code from the view as possible.
Tip
You will be writing a ton of forms in your career. Showing the error for a given record and attribute should be maximally comfortable for your views β nudge yourself toward a helper with a nice API, like
error_message(@movie, :title). -
Style your form so that when a field is invalid, the label and error message is colored red. In addition, an invalid field control should have a red border.
Tip
Rails adds an extra element around invalid inputs to help with styling. Inspect an invalid field in your browser to see how it works.
-
Add support for a positive and negative flash message Show archive.org snapshot in your application layout. When a form could be saved successfully, show a flash like "Movie saved". When a form is re-rendered due to a validation error, show a flash like "Could not save".
Tip
Unlike
@variablesset by a controller, flash messages persist to the next request. This is to support the case of setting a flash and then redirect.If you have trouble with flash messages appearing longer than they should, you can set them to
flash.nowor clear all flash messages after displaying them by usingflash.clear.
Custom validations
Add the following feature to your MovieBD:
- The release year field of Movies should always be a number. It can also be missing (
nil). - Movies have a film type. This is a string that is either
"black_and_white"or"color". - The default film type is
"color". - In the movie form we can switch between black and white and color movies using a
<select>. - Add a validation that ensures that
release_year >= 1895for black and white movies andrelease_year >= 1917for color movies.
Add tests for your validation. Make sure you have a test for each case.
Hint
- The year rule spans two attributes β write a custom validation method Show archive.org snapshot instead of bending a built-in validation into shape.
- For the
<select>, see Making select boxes with ease Show archive.org snapshot in the Form Helpers guide.