Partial Validation in Rails

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: , , , ,


About Claudio

Claudio Marai is a co-founder of DevInterface.

After graduating in Computer Science has contributed to develop complex web applications based on Java/J2EE and desktop applications with the. NET framework for the Ministry of Justice and ultimately for the banking ambit.

The passion for web in recent years has led him to be interested in more modern frameworks such as Ruby on Rails and Django, and to a development approach based on agile methodologies such as eXtreme Programming and SCRUM.

About DevInterface

We are an information and communication technology agency. Our mission is to provide web application development, design services and communication strategies. We specialize in building web applications with modern and efficient frameworks.

Related Post

5 Responses to “Partial Validation in Rails”

  1. manish says:

    nice post. many a times we do waste time for such small issues… !

  2. 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.

  3. Claudio says:

    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.

  4. Mario T. Lanza says:

    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.

  5. Dripable says:

    Thats some scholastic read…

Leave a Reply

Insert code beetween <code lang="ruby"> and </code>

Copyright 2012 DevInterface s.n.c.

DevInterface Blog is proudly powered by WordPress