Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 811 Vote(s) - 3.46 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to validate a nested model object based on the state of the parent object?

#1
I am writing a wizard form in rails; e.g. multiple input pages for one model object.

The basics of my approach are those described in Ryan Bates' Multistep form railscast :

[To see links please register here]

(in case anyone wants to know the WHY behind some of the code below).

The objects under scrutiny here are "Participant", which has one "Address"

My problem is that I only want to validate the nested object (Address) when the user is trying to progress past the address input screen. This is currently tracked via an attribute on the Participant model called "current_step"

So I have a Participant:

class Participant < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
validates_presence_of :first_name, :last_name, :if => self.current_step == "name"
...
def steps = %w[ name address confirm ] # the steps the wizard will follow
end

And an Address:

class Address < ActiveRecord::Base
belongs_to :participant
validates_presence_of :address1, :state, :suburb, :postcode #, :if => participant.current_step == "address"
end

The principle of this approach is the "create" action is called on the controller (not shown) for each step of the wizard, and it only validates a subset of the model when each step is processed.

Currently, when I complete the first screen ("name") and try and go onto the address step, the address validation is getting triggered, and I get sent back to the "name" screen with validation errors for blank address details.

So I have tried a number of approaches here, the final part of which was the commented out condition on Address validation shown above - this I've found doesn't work as I'm only building Participant->Address objects, but not saving them. Therefore `@participant.address` gets me the address object, but `@participant.address.participant` is null as the Address does not yet have a participant_id foreign key to lookup it's parent.

The reason for my struggles appears to be the inclusion of the super-handy `accepts_nested_attributes_for` method. I was expecting to use a `validates_associated` to be doing the validation, but I see that the `accepts_nested_attributes_for` tag both propagates form parameters nicely to create nested model objects, but also ensures the `participant#valid?` method calls down to the address validation in ALL situations.

So my dilemma is how to best use the `participant#valid?` method to valid the partially complete model, based off the `current_step` parameter in the participant ?

EDIT - updated to remove extra info, and distill down to the core problem
Reply

#2
Add a virtual attribute on your `Address` model:


class Address < ActiveRecord::Base
belongs_to :participant
attr_accessor :skip_validation
validates_presence_of :address1, :state, :suburb, :postcode,
:unless => :skip_validation
end

Set the virtual attribute on the address object when `current_step` is set.

class Participant < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
attr_accessor :current_step
validates_presence_of :first_name, :last_name,
:if => lambda {|r| r.current_step == "name"}


def current_step=(value)
unless (value == "address")
address.skip_validation = true
end
@current_step = value
end
end
Reply

#3
Although the ActiveRecord "validate" methods are very convenient, there's nothing to stop you from "rolling your own", perhaps using `before_save` hooks. You could replace your `validates_presence_of` validator on `Address` with a `before_save` hook which does validation only under certain conditions. Then `accepts_nested_attributes_for` presumably wouldn't see it.
Reply

#4
How about just assigning it in your controller before you call `participant.valid?`:

@participant.address.participant = @participant
Reply



Forum Jump:


Users browsing this thread:
2 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through