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 powered by Disqus