Arun Shyam
Arun Shyam

Reputation: 581

two forms one model in Ruby, separate validations for each

I have three fields in one form and two fields in another (same as the earlier form, but just missing one field). I want to validate only two fields in the smaller form, but the issue is that it is validating all the three.

I have written the following logic:

**

class User < ActiveRecord::Base
  validate :validate_form #for form with 2 fields
  private
    def validate_form
      if :classify_create
        self.errors.add(:weight, "need weight") if weight.blank?
        self.errors.add(:height, "need height") if height.blank?
      end
  end

  #   Validations of attributes (for form with three fields)
  validates :weight, :presence => true

  validates :height, :presence => true

  validates :gender, :presence => true
end

**

and this is my controller action: basically I have written two separate creates:

**# for form with two fields
  def classify
    @user  = User.new
    @title = "Classify"
  end

  def classify_create
    @user  = User.where("weight = ? and height = ?", params[:weight] ,params[:height])
  end

# for form with three fields
  def new
    @user  = User.new
    @title = "Train"
  end

  def create
    @user = User.new(params[:user])
    if @user.save
       redirect_to @user
    else
      @title = "Train"
      render 'new'
    end
  end**

When I submit to the two field form, it gives me an error for gender too and redirects to the page with form having three fields. How should I go about it?

Any help will be appreciated.

Regards, Arun

Upvotes: 1

Views: 736

Answers (2)

Substantial
Substantial

Reputation: 6682

First, I would not use classify as a method name. You may conflict with a core inflector provided by ActiveSupport. Call it classification or something.

Second, your validation is running on if @user.save in the create method.

In classify_create you use User.where(...) which is a finder method. You're pulling a matching record and setting it to @user. This does not run validation, yet you receive validation errors. You are posting to create, not classify_create. Bad routes will cause this.

Let's address conditional validation first. In your User model, create a variable to act as a bypass switch for your gender validation. Then tell your validation to check if this bypass switch is false before running:

User < ActiveRecord::Base
  attr_accessor :skip_gender  # defaults to nil (false)
  # ...

  validates :gender, :presence => true, :if => :validate_gender? # validate if...
  # ...

  private
  def validate_gender?
    !self.skip_gender # true = run validation, false = skip validation
  end
  # ...
end

Next, clean up your controller. Write two create methods, one setting the switch, one not. (This isn't DRY):

def new_classification
  # for form with two fields
  @user  = User.new
  @title = "Classify"
end

def new
  # for form with three fields
  @user  = User.new
  @title = "Train"
end

def create
  @user = User.new(params[:user])
  if @user.save
    redirect_to @user
  else
    render :action => 'new' # render three-field form
  end
end

def create_classification
  @user  = User.where(:weight => params[:weight], :height => params[:height])
  # ... do something with @user ...
  @user.skip_gender = true # tell @user to skip gender validation
  if @user.save
    redirect_to @user
  else
    render :action => 'new_classification' # render two-field form
  end      
end

Next, adjust config/routes.rb to specify routes to your custom methods.

resources :users do
  member do
    get 'new_classification',     :to => 'users#new_classification', \
                                  :as => :new_classification_for
    post 'create_classification', :to => 'users#create_classification', \
                                  :as => :create_classification_for
  end
end

Now change your two-field form view. Specify where your form is submitted to.

<%= form_for @user, :url => create_classification_for_user_url(@user) do |f| %>

That should get you by with what you have...

Upvotes: 3

mu is too short
mu is too short

Reputation: 434945

Your problem is two-fold:

  1. You're trying to use one controller for two distinct actions.
  2. The Rails validation model is somewhat limited and inflexible, there should be separate validation passes for controller methods and models.

The easy solution is to kludge around the limitations with a separate controller:

def create_genderless
    # Force the issue to an explicit "unknown" state so that
    # "unknown" and "missing" can be differentiated.
    params[:user][:gender] = 'U'
    # And then punt to the existing `create` method.
    create
end

Then a bit more validation in your model for good measure

class User < ActiveRecord::Base
    validates :gender, :inclusion => { :in => %w[M F U] }
    #...
end

Then update your forms to use UserController#create or UserController#create_genderless as appropriate.

Upvotes: 0

Related Questions