views:

394

answers:

2

I'm trying to access my parent model in my child model when validating. I found something about an inverse property on the has_one, but my Rails 2.3.5 doesn't recognize it, so it must have never made it into the release. I'm not sure if it's exactly what I need though.

I want to validate the child conditionally based on parent attributes. My Parent model has already been created. If the child hasn't been created when I update_attributes on the parent, then it doesn't have access to the parent. I'm wondering how I can access this parent. It should be easy, something like parent.build_child sets the parent_id of the child model, why is it not doing it when building the child for accepts_nested_attributes_for?

For Example:

class Parent < AR
  has_one :child
  accepts_nested_attributes_for :child
end
class Child < AR
  belongs_to :parent
  validates_presence_of :name, :if => :some_method

  def some_method
    return self.parent.some_condition   # => undefined method `some_condition' for nil:NilClass
  end
end

My form is standard:

<% form_for @parent do |f| %>
  <% f.fields_for :child do |c| %>
    <%= c.name %>
  <% end %>
<% end %>

With an update method

def update
  @parent = Parent.find(params[:id])
  @parent.update_attributes(params[:parent])   # => this is where my child validations take place
end
A: 

check these sites, maybe they'll help you...

http://stackoverflow.com/questions/2102724/rails-nested-attributes-association-validation-failing

http://stackoverflow.com/questions/935650/accepts-nested-attributes-for-child-association-validation-failing

http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes

it seems, rails will assign parent_id after child validation succeeds. (as parent has an id after it's saved)

maybe worth trying this:

child.parent.some_condition

instead of self.parent.some_condition ... who knows...

kulpae
+1  A: 

You cannot do this because in-memory child doesn't know the parent its assigned to. It only knows after save. For example.

child = parent.build_child
parent.child # => child
child.parent # => nil

# BUT
child.parent = parent
child.parent # => parent
parent.child # => child

So you can kind of force this behavior by doing reverse association manually. For example

def child_with_inverse_assignment=(child)
  child.parent = self
  self.child_without_inverse_assignment = child
end

def build_child_with_inverse_assignment(*args)
  build_child_without_inverse_assignment(*args)
  child.parent = self
  child
end

def create_child_with_inverse_assignment(*args)
  create_child_without_inverse_assignment(*args)
  child.parent = self
  child
end

alias_method_chain :"child=", :inverse_assignment
alias_method_chain :build_child, :inverse_assignment
alias_method_chain :create_child, :inverse_assignment

If you really find it necessary.

P.S. The reason it's not doing it now is because it's not too easy. It needs to be explicitly told how to access parent/child in each particular case. A comprehensive approach with identity map would've solved it, but for newer version there's :inverse_of workaround. Some discussions like this one took place on newsgroups.

hakunin