Not logged in (Log in or Sign up)

unwwwritten

has_many problems with validations

Today's gotcha has to do with using has_many and validations within the child model.

In my use-case I need to create a parent record with one or more children.

The parent is not valid without children and a child is not valid without a parent.

   1  class Parent < ActiveRecord::Base
   2    has_many :children, :dependent => :destroy
   3    validate :must_have_children
   4  protected
   5    def must_have_children
   6      errors.add_to_base('must have at least one child') if children.blank?
   7    end
   8  end
   9  
  10  class Child < ActiveRecord::Base
  11    belongs_to :parent
  12    validates_presence_of :parent_id
  13  end

So, how do you create them (taking into account the circular validation dependency above)?

   1  # create the parent
   2  @parent = Parent.new(params[:parent])
   3  # then, add the children
   4  if children = params[:children]
   5    children.each do |child|
   6      # the child params will not include a parent_id (since it does not exist yet)
   7      @parent.children.build(child)
   8    end
   9  end
  10  # saving the parent should save the children
  11  @parent.save!

Unfortunately, this would result in an error, since the parent of the has_many attempts to validate the children before saving itself. Since the parent has not been saved yet, it has no id. This means that the parent_id is not set in the child, which results in a validation failure.

Fortunately, there is an easy fix workaround...

In your controller code, where you are creating the children... (temporarily) set an explicit parent_id.

   1      @parent.children.build(child.merge(:parent_id => 0))

In my case I chose to set it to 0, but as long as it is set, the child will validate. This will allow the parent to be saved, which will assign an id value. That id will then be propagated to the children and BAM! we've created a parent and one or more children with a single save (or save!) call.

blog comments powered by Disqus