Partial Validation in Rails
12:45 PMDevelopment, RubyClaudio
It happens sometimes that you have to implement creation or editing of a model splitted on more than one form.
A classic case is a registration form divided into two steps or a guided wizard with several pages.
This situation presents us with a problem: when and how to validate data?
The first idea that may come to mind is simply to leave it all validations in the last page when you go to create or update the model. But this is also the least elegant solutions, both in terms of code than usability.
What to do?
I present a technique that I adopted in similar circumstances to achieve “partial validation of a model”.
The idea is to implement a method that validates only the attributes present at every step.
We begin by defining, within our model, a method to validate a single attribute:
1 2 3 4 5 6 7 | def self.valid_attribute?(attrib, value) mock = self.new(attrib => value) unless mock.valid? return !mock.errors.on(attrib).present? end true end |
This method uses of a mock object to simulate the validation of model and after it verifies whether the hash of validation errors contains the attribute (attrib) that interests us.
If not, the method simply return true, regardless of other errors, thus overcoming the validation (partial!) of the model.
But let’s see an example.
Suppose we want to create a registration form in two steps, where in the first step login and password are required and in the second one anagraphic data are required.
We also add the constraint that registration is to be considered complete only when all data are provided.
In such a situation it is convenient to use partial validation.
We go to define the user model as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class User < ActiveRecord::Base validates_presence_of :login, :password, :email, :name, :surname, :street, :city, :country validates_uniqueness_of :login #Some code here.. def self.valid_attribute?(attrib, value) mock = self.new(attrib => value) unless mock.valid? return !mock.errors.on(attrib).present? end true end end |
Now suppose we have a first form that requires users to enter login, password and email and a second form that asks for personal information.
The two forms call respectively the methods step1 and step2 of the UserController.
In the step1 we can now use the valid_attribute? method defined above to validate only the attributes that interest us.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | class UserController < ApplicationController def new @user = User.new end def step1 @user = User.new(params[:user]) if !valid_attribute?(:login, user.login) or !valid_attribute?(:password, user.password) or !valid_attribute?(:email, user.email) #... generate an error message and re-render the form #... else #.. Save user in session and go to step session[:new_user] = @user redirect_to :action => "step2" end end def step2 # Get user from session @user = session[:new_user] # Update attributes with anagraphic data @user.update_attributes(params[:user]) if @user.save flash[:notice] = 'user created' # Clear Session session[:new_user] = nil redirect_to(users_url) else render :action => "step2" end end # Other methods... end |
In the step2 we execute instead the save method the runs the “classic” Rails validation, cause it is the last step of our registration wizard and is here where you actually creates the user.
With the technique shown you can add as many steps you want, with the care of not ever save the model until it has reached the last step.
If you need to make more wizards inside your application, may be convenient to move the validate_attribute? method in a module to be used as mixin classes where you need it.
Obviously this is only one possible solution to the partial validation problem.
Have you ever faced the partial validation problem?
And if so, how did you solved it?
Tags: mixin, partial validation, rails, ruby on rails, validation

















nice post. many a times we do waste time for such small issues… !
It seems like you can manage the validations with a state machine. Pluginaweek’s state_machine allows you to declare certain validations within a specific state so step1 could be a state with some validations and step2 would be the state with the superset of validations. Check out the ActiveRecord portion of the state_machine readme for an example of declaring validations in a specific state http://github.com/pluginaweek/state_machine . This has the advantage of placing all validation logic in the model allowing the controller to confirm to the same old valid? api.
Hi Sandro, I agree with you.
It’s possible to use the state_machine plugin but in this example i wanted to show a generic method to do partial validation. I also wanted to show a “Ruby Way” manner to think and write code.
More, if you want to respect the “fat model skinny controller” pattern you can write some methods like valid_step1, valid_step2 etc. in the model and call them from your controller.
Another situation where this approach can be usefull is, for example, if you have more models involved and you want to make some partial validation on one or all of them. In complex situation like this, be able to do partial validation “by yourself” can give you more flexibilty.
Off the top of my head…
What about simply populating the model with all data available thus far (after each step) and calling validate, but presenting to the user only the errors based on the fields seen thus far? The model will indicate it’s not ready (invalid? = true) which is fine; it’s not. More importantly: we just want to prevent the interface from displaying the failed validations for which the user hasn’t yet had the opportunity to address.
Thats some scholastic read…