Rails accepts_nested_attributes_for validation on transactional object

nested, ruby-on-rails, validation

Solution

I think you should use validates_associated for this

with the following validation in Child

validates :value, :presence => true, :if => "self.parent.status == 'validate_children'"

Problem

I'm struggling since some hours in order to make validations of nested attributes work in my rails app. A small caveat is that I have to validate nested attributes dynamically based off of their parent's attributes, as the amount of info required changes over time according to where in the process the parent is. So here's my setup: I have a parent with many different associated models and I want to validate subsequently nested attributes of those every time I save the parent. Given the fact that validations change dynamically, I had to write a custom validation method in the model: ``` class Parent < ActiveRecord::Base attr_accessible :children_attributes, :status has_many :children accepts_nested_attributes_for :children validate :validate_nested_attributes def validate_nested_attributes children.each do |child| child.descriptions.each do |description| errors.add(:base, "Child description value cant be blank") if description.value.blank? && parent.status == 'validate_children' end end end end class Child < ActiveRecord::Base attr_accessible :descriptions_attributes, :status has_many :descriptions belongs_to :parent accepts_nested_attributes_for :descriptions end ``` In my controller I call update_attributes on the parent when I want to save. Now the problem is that, apparently, rails runs the validations against the database and not against the object that was modified by the user or the controller. So what might happen is that a child's value is erased by a user and the validations will pass, while later validations will not pass because the item in the database is not valid. Here's a quick example of this scenario: ``` parent = Parent.create({:status => 'validate_children', :children_attributes => {0 => {:descriptions_attributes => { 0 => {:value => 'Not blank!'}}}}) #true parent.update_attributes({:children_attributes => {0 => {:descriptions_attributes => { 0 => {:value => nil}}}}) #true!! / since child.value.blank? reads the database and returns false parent.update_attributes({:children_attributes => {0 => {:descriptions_attributes => { 0 => {:value => 'Not blank!'}}}}) #false, same reason as above ``` The validation works for first-level associations, e.g. if a Child has a 'value' attribute, I could run a validation the way I do. The problem is with deep associations that apparently cannot be validated before saving. Could anyone point me in the right direction of how to solve this? The only way I currently see is by saving records, validating them afterwards and deleting / reverting them if the validation fails, but I am honestly hoping for something more clean. Thank you all in advance! SOLUTION So it turns out I was running validations on deep nested models by referencing those directly in the custom validation, this way: ``` class Parent < ActiveRecord::Base [...] has_many :descriptions, :through => :children [...] def validate_nested_attributes descriptions.each do |description| [...] end end end ``` Which for some reason leads to the problems I was having above. Thanks Santosh for testing my example code and reporting it was working, this pointed me in the right direction to figure this out. For future reference, the code in the original question works for this sort of dynamic, deeply-nested validations.

Original source