Rails accepts_nested_attributes_for child doesn't have parent set when validating
Asked Answered
K

4

11

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
Kossuth answered 9/2, 2010 at 4:27 Comment(0)
C
14

I had basically the same problem with Rails 3.2. As suggested in the question, adding the inverse_of option to the parent's association fixed it for me.

Applied to your example:

class Parent < AR
  has_one :child, inverse_of: :parent
  accepts_nested_attributes_for :child
end

class Child < AR
  belongs_to :parent, inverse_of: :child
  validates_presence_of :name, :if => :some_method

  def some_method
    return self.parent.some_condition   # => undefined method `some_condition' for nil:NilClass
  end
end
Corpus answered 25/4, 2013 at 12:22 Comment(0)
D
8

I had a similar problem: Ruby on Rails - nested attributes: How do I access the parent model from child model

This is how I solved it eventually; by setting parent on callback

class Parent < AR
  has_one :child, :before_add => :set_nest
  accepts_nested_attributes_for :child

private
  def set_nest(child)
    child.parent ||= self
  end
end
Debark answered 19/2, 2011 at 6:43 Comment(1)
I had the same error as the op, but when I tried this method I got "Unknown key(s): before_add" ?Altercation
J
7

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.

Jenks answered 8/7, 2010 at 2:46 Comment(0)
C
0

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

Rails Nested Attributes Association Validation Failing

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...

Calceolaria answered 7/7, 2010 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.