Update : Apparently this problem is specific to has_one relationships. If you’re updating a nested model through a has_many relationship then you may not have this problem.

I’ve just solved a little gotcha with the new nested models feature which shipped with rails 2.3. If you’ve been having the following error when you try to render your model/new forms then read on…

1
2
3
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.new_record?

What is causing the nil which knocks on to the NoMethodError on nil.new_record?

By following the instructions from rails guides I’d hit a brick wall and kept getting the error above whenever I’d try and render my new form. My code looked like so :

First the two models….

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# app/models/student.rb

# == Schema Information
# Schema version: 20090317153604
#
# Table name: students
#
#  id         :integer         not null, primary key
#  name       :string(255)
#  age        :integer
#  created_at :datetime
#  updated_at :datetime
#

class Student < ActiveRecord::Base
  has_one :address
  
  accepts_nested_attributes_for :address
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# app/models/address.rb

# == Schema Information
# Schema version: 20090317153604
#
# Table name: addresses
#
#  id           :integer         not null, primary key
#  house_number :integer
#  road         :string(255)
#  student_id   :integer
#  created_at   :datetime
#  updated_at   :datetime
#

class Address < ActiveRecord::Base
  belongs_to :student
end

And the view….

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# app/views/students/new.html.erb

<h1>New student</h1>

<% form_for(@student) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :age %><br />
    <%= f.text_field :age %>
  </p>

  <% f.fields_for :address do |a| %>
    <p>
      <%= a.label :house_number %>
      <%= a.text_field :house_number %>
    </p>
    <p>
      <%= a.label :road %>
      <%= a.text_field :road %>
    </p>
  <% end %>

  <p>
    <%= f.submit 'Create' %>
  </p>
<% end %>

<%= link_to 'Back', students_path %>

The controller was left untouched.

As far as I can tell from the rails guide, unless I’m missing something really obvious, this is all that is needed. However I still get nil.new_record? errors when I try and hit that view.

The solution!

Thanks to the complex forms examples I was able to see where I was going wrong and put things right. It’s such a good idea to have working code examples of new features, thanks for that guys.

It turns out I needed to call build_address on my new student object. Makes sense I suppose. Otherwise we’re returning a nil from @student.address so we need to put a new object in @student.address so that its attributes can eventually be set.

And here’s how it works…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# app/views/students/new.html.erb

# Code from before and after the nested model has been removed for brevity

<% @student.build_address unless @student.address %>
<% f.fields_for :address do |a| %>
  <p>
    <%= a.label :house_number %>
    <%= a.text_field :house_number %>
  </p>
  <p>
    <%= a.label :road %>
    <%= a.text_field :road %>
  </p>
<% end %>

I assume that @student.build_address is the same as @student.address = Address.new. So it gives you a new blank address object, ready for the attributes to be pushed into. And now when rails calls new_record? on this object we don’t get a NoMethodError any more.

References

Comments

  1. Sean Cannon |

    Thank you, thank you, thank you. I have been banging my head against my desk for several hours trying to come up with a solution to this problem! This works great!

  2. http://antonjenkins.myopenid.com |

    Glad I could help out! It's certainly not an obvious problem to solve. I tried updating the Rails 2.3 release notes to mention this and my changes got revoked because they wanted to keep the release notes static, even though they are misleading. I'm not sure I totally see the logic there if people are tripping up on this, but there you go!

    At least this page is the top google result when searching for "rails nested model form NoMethodError" so that's something.

  3. beenhero |

    thanks for the hint, save me a lot of time.

  4. Victor |

    Anton, I found another solution for this. I added code to initializers to avoid call build child node in view or controller

    1
    2
    3
    4
    5
    
    class << nil
      def new_record?
        true
      end
    end
    
    What you think about?

  5. http://antonjenkins.myopenid.com |

    @victor : Does this actually work? I imagine it would stop you getting the NoMethodError when new_record? is called but it isn't going to initialise the child record like the build_child method does. Will Active Record do this anyway? I don't know.

    I'm guessing this will also add a new_record? method to every class. That wouldn't override all instances of ActiveRecord::Base would it, so that all instances return true for new_record??

    I'd be wary of doing it that way to be honest. Try it and if it works then great. My knowledge of Active Record's insides isn't comprehensive enough to say if it could potentially trip things up so I'd be careful.

  6. http://beautifulpixel.com |

    @Victor, that is a very bad idea. Instead of fixing the problem, you are monkeypatching a core ruby class to basically ignore an error. The code still has a problem in that you are displaying form fields for nil. Making nil think its a new record doesn't change that.

  7. rociiu |

    Thanks a lot ! Save me time .

  8. Daniel |

    So i wouldn't have to call build_address in the view or the controller i override the model's initialize method:

    1
    2
    3
    4
    
      def initialize(attributes=nil)
        super
        self.build_address unless self.address
      end
    

    Works for me and should they ever patch the issue you need only remove it from the model and not where ever it got peppered in your controllers and views.

  9. Aninda |

    thank you. thank you.

  10. Kenny |

    Thank you so much.

  11. KAN |

    Thank you very very much :O)

    One question though, what if I have more than one model in my form?

    Thanks... NB: I'm very newbie at Rails, but loving it

  12. http://antonjenkins.myopenid.com |

    @KAN, I guess for more than one nested model you would just do the same as you did for the first nested model - build the object (if needed) and render the fields for it:

    1
    2
    3
    4
    
    <% @student.build_another_nested_model unless @student.another_nested_model %>
    <% f.fields_for :another_nested_model do |m| %>
    etc etc....
    <% end %> 
    

    I've not tested updating more than one nested model but I can't see any reason why it shouldn't work.

    I'm glad you found this post useful. Thanks for the feedback! :o)

  13. Richard Shank |

    Just a heads up, you can run into problems if you have attr_accessible in your parent model.

  14. Richard Shank |

    Sorry about that, I just realized that you can use attr_accessible in your parent, but you must add :childmodelname_attributes to the parent attr_accessible

  15. Josh |

    Cheers!

    You can also run this in the "new" method of your controller if you want to keep it out of your view.

  16. http://https://www.google.com/accounts/o8/id |

    i am too on the thank you band wagon. Of course you have to build the object by that stumps me as to why it is only for the has_one. Does Active Record support the has_many when nesting fields?

  17. Katz |

    I still don't get the has_one with accepts_nested_attributes_for. Seems like it's not working well yet.

  18. http://antonjenkins.myopenid.com |

    I believe the has_many relationships return an empty array with a bit of magic rolled in so that when you try and set the attributes on this empty array the class can see what you are trying to do and automatically creates an empty instance for you.

    However has_one just returns nil. No magic going on there!

    I "think" that's roughly why we have this disparity between has_one and has_many in nested forms. I've not had a sniff at the source, I'm just going on what I've read in forums.

  19. Jan |

    Thank you! You saved me hours of bashing my head against a wall.

  20. Jan |

    Thank you! You saved me hours of bashing my head against a wall.

  21. Scott |

    This helped a ton, thank you!

  22. JurgenH |

    Still helping out! Thanks.

  23. adac |

    You finally saved my day!! :)

    Well isn't that a bug anyways? All the other examples on nested attributes do never mention that one would need this line (<% @student.build_address unless @student.address %>
    ) to get complex form working

    well however: It works and I'm truly happy again

  24. Zippy |

    I'm getting a 'method_missing' error for 'build_address'. Doesn't that method need to get defined somewhere? Is it a custom method or is it generated dynamically by Rails somewhere along the line?

  25. http://anton.jenkins.myopenid.com |

    @Zippy, the build_<model name> methods are dynamically generated by Rails. I'm assuming that you are trying build_ and then whatever your model name is like build_robot or build_fridge?

    The build_<model name> method is mentioned in the ActiveRecord association class methods here :

    Rails API Documentation for ActiveRecord Associations

    Maybe check to make sure you have defined your relationships between models correctly :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    class Student < ActiveRecord::Base
      has_one :address
      
      accepts_nested_attributes_for :address
    end
    
    class Address < ActiveRecord::Base
      belongs_to :student
    end
    

  26. Zippy |

    @anton

    Thanks for the reply. Yes, I was changing the method name to match my models. I was trying a :has_one => :through type of relationship, which I think might have been the issue. I'll read up on AR Associations from the link you provided and see what's up.

  27. http://anton.jenkins.myopenid.com |

    @Zippy, I've not tried doing it with a :has_one => :through relationship but I think you're right in assuming this is probably the root of the problem.

    You could try having a look to see if it's been covered with the examples?

    Complex form examples

    Good luck!

  28. LeDuc |

    Thank you so much.

  29. Pete |

    This was really helpful. I was getting nil objects when I was trying to create nested forms for FedEx shipments. Now all is well.

  30. Abhishek Shukla |

    Thanks a ton that really saved my life... :)

  31. Senthil |

    Excellent...been having that trouble for days now. Thanks!

  32. Senthil |

    I tried making 'Remove' for Tags hide just like Task by adding the
    <%= remove_link_unless_new_record(tag_form) %> but it doesn't seem to work.

    Thanks

Post a comment


(lesstile enabled - surround code blocks with ---)