Rails 2.3 : Nested model forms and nil.new_record?
Anton Jenkins | March 18, 2009
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
- Rails Guides : http://guides.rubyonrails.org/2_3_release_notes.html#nested-object-forms
- The Ruby on Rails blog : http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
- Complex form examples: http://github.com/alloy/complex-form-examples/commits/master/











