What's New in Edge Rails: Nested Object Forms

Posted by ryan
at 9:59 AM on Sunday, February 01, 2009

This feature is scheduled for: Rails v2.3

We were all teased a few months ago about the possibility of finally solving the nested model/complex forms problem in Rails, but were then cruelly notified that it wasn’t quite ready for prime time. But our day has come – the most requested feature for Rails 2.3, the ability to handle multiple models in a single form, is here.

This API update was added after the original commit. I’ve updated the examples here to account for this.

This feature has already been written about on the Rails Blog quite well by Eloy Duran, the committer of this fine feature, so I’ll try not to replicate what’s already out there. However, here’s a basic rundown of what you need to do to get your models nested-form capable.

Step 1: Notify Your Model of Nest-able Associations

The first step is to tell your models which of their associations will be able to receive nested attributes. For all associations you want exposed in nested forms you’ll need to use accepts_nested_attributes_for:

1
2
3
4
5
6
7
8
9
class Person < ActiveRecord::Base

  validates_presence_of :name

  has_many :children, :class_name => 'Person'
  accepts_nested_attributes_for :children, :allow_destroy => true
    # can also be used on has_one etc.. associations

end

With this bit in place, you can now directly create, edit and delete children from a person:

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
# Add a new child to this person
@person.children_attributes = [ { :name => 'Son' } ]
@person.children #=> [ <#Person: name: 'Son'> ]
@person.children.clear

# Add two new children to this person
@person.children_attributes =
  [ { :name => 'Son' }, { :name => 'Daughter' } ]
@person.save
@person.children #=> [ <#Person: name: 'Son'>, <#Person: name: 'Daughter'> ]

# Edit the son (assuming id == 1)
@person.children_attributes = [ { :id => 1, :name => 'Lad' } ]
@person.save
  #=> the son's name is now 'Lad'

# Edit the daughter (id == 2) and add a new offspring
@person.children_attributes =
  [ { :id => 2, :name => 'Lassie' }, { :name => 'Pat' } ]
@person.save
  #=> the daughter's name is now 'Lassie' and there's a new offspring called 'Pat'

# Remove Pat (id = 3), we don't like him/her
@person.children_attributes = [ :id => 3, '_delete' => '1' } ]  # !!! In Rails 3 this is now "_destroy" !!!
@person.save
  #=> Pat is now deleted
You’ll want to take away a few things from these examples.
  • To support both the creation of new objects and the editing of existing ones we have to use an array of hashes for one-to-many associations or a single hash for one-to-one associations. If no :id property exists then it is assumed to represent a nested model to create.
  • To delete an existing nested model, use this format: [ :id => pk, '_delete' => '1' } ] where the value of _delete evaluates to anything true. You must also set the accepts_nested_attributes_for option :allow_destroy to true as that capability is turned off by default.
  • In Rails 3 the aforementioned _delete param is now called _destroy

While this may appear a bit hackish when you’re used to dealing with the pleasantries of a rich object model and with ActiveRecord’s associations, this provides the foundation for a seamless transition to the view where you need to create your nested model forms…

Step 2: Create a Nested Model Form

In the view, simply use fields_for on these nested models to expose the fields for each such model:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<% form_for @person do |person_form| %>

  <%= person_form.label :name %>
  <%= person_form.text_field :name %>

  <% person_form.fields_for :children do |child_form| %>

    <%= child_form.label :name %>
    <%= child_form.text_field :name %>

    <% unless child_form.object.new_record? %>
      <% # Don't forget to name both of these '_destroy' in Rails 3 %>
      <%= child_form.check_box '_delete' %>
      <%= child_form.label '_delete', 'Remove' %>
    <% end %>

  <% end %>

  <%= submit_tag %>
<% end %>

This will create a form with all the form fields necessary for submitting to a RESTful controller, transparently pushing your children_attributes onto the person.

If there are any validation errors on a child, they will be added to person.errors, and nothing will save if any of the children fail (i.e. fully transactional).

A few notes that might be useful to you:
  • Using fields_for on a has_many association automatically executes once for each nested model present, so think of yourself as being inside a loop when building your child_form
  • If you ever need to change behavior based on the nested model currently in scope, it can be accessed via child_form.object. In this example we use child_form.object.new_record? to determine whether or not to display the delete checkbox (as that only makes sense on an existing record).

Step 3: In Your Controllers … Do Nothing

The third step should be the easiest, because we’re all dealing with purely RESTful controllers, right? The beauty of this solution is that it takes your controllers out of the mix and makes standard for submissions work perfectly with no interference at the controller level. Just so there’s no confusion, here’s how your create and update actions will look:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PersonController < ApplicationController

  def create
    @person = Person.new(params[:person])
    @person.save ? redirect_to(person_path(@person)) : render(:action => :new)
  end

  def update
    @person = Person.find(params[:id])
    @person.update_attributes(params[:person]) ?
      redirect_to(person_path(@person)) : render(:action => :edit)
  end

end

Not a peep of those pesky nested models – with the rich support for nested objects at the model layer, it just works!

Extras

As with most powerful features, there are few little tweaks you may find yourself needing.

Default Create Form Fields

Often times you’ll want to have the form displayed with empty fields for easily creating a new nested model. For example, when a user goes to create a new person I want there to be fields for creating a new child already displayed.

Since the person object is brand new they have an empty children collection and no child_form fields will be displayed. There are two ways to get around this:

You can build a new nested object on the controller side (i.e. in the new action):

1
2
3
4
5
  def new
    @person = Person.new
    @person.children.build
    # ...
  end

which will cause there to be empty child_form fields displayed as desired. Or you can do it on the view side with a view helper:

1
2
3
4
5
6
7
module ApplicationHelper
  def setup_person(person)
    returning(person) do |p|
      p.children.build if p.children.empty?
    end
  end
end

Which can then be used within form_for to setup the person to the correct form state:

1
2
3
<% form_for setup_person(@person) do |person_form| %>
  <!-- ... -->
<% end %>

I prefer this view-helper approach as it really is a view concern (whether or not to display the form fields to create a new nested object by default).

Specify When Nested Models get Built

If you do have empty nested model form fields displayed by default, you’ll run into the issue where the user submits the form with no values filled in and you have to decide if you want to treat that as somebody trying to create a new nested item with no values, or if that means that no new nested item was submitted. Quite often you just want to ignore the submissions with no nested field values filled out.

Although I would have expected this to be default behavior, you need to manually specify that submissions with empty nested values are ignored using the :reject_if option of accepts_nested_attributes_for:

Note: You now have the option to use :all_blank to ignore the item if all properties are blank. See below for an updated example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person < ActiveRecord::Base

  validates_presence_of :name

  has_many :children, :class_name => 'Person'

  # This will prevent children_attributes with all empty values to be ignored
  accepts_nested_attributes_for :children,
    :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }

  # This does the same thing using :all_blank
  accepts_nested_attributes_for :children, :reject_if => :all_blank

end
1
2
3
@person.children_attributes = [ { :name => '' } ]
@person.save
@person.children.count #=> 0

This is also useful if you have boolean values in your nested model fields (since, as a checkbox, ‘0’ will be submitted if there’s no value):

1
2
3
4
5
6
7
8
9
10
11
12
class Person < ActiveRecord::Base

  validates_presence_of :name, :bad

  has_many :children, :class_name => 'Person'

  # This will prevent empty checkboxes submitted for a child to be
  # construed as a submission
  accepts_nested_attributes_for :children,
    :reject_if => proc { |attrs| attrs['bad'] == '0' && attrs['name'].blank? }

end
1
2
3
@person.children_attributes = [ { :name => '', :bad => '0' } ]
@person.save
@person.children.count #=> 0

Dynamically Adding Nested Form Fields

If you want to allow the addition of a large number of nested models via your HTML forms, one option is to just have several empty nested forms displayed by default. This is a little unappealing, however. The far slicker option is to use javascript to dynamically display the new nested form on the user’s request.

Eloy has a great example application setup on GitHub outlining how this works, and I’ll let you take a peek over there to see how to wire up your dynamic nested form additions.

Eloy’s example app is also a great place to see how the whole thing works, end-to-end. By far the best resource out there.

Conclusion

So there it is, Rails’ most requested new feature in the flesh and blood. I’m not sure if my experiences are indicative of everybody else’s, but this is a godsend for me. Many thanks to all the folks involved with this functionality (if you don’t know who they are, check out the next section which links to a bunch of great resources). A really great effort by the community.

Resources

tags: ruby, rubyonrails

Comments

Leave a response

  1. SvoopFebruary 01, 2009 @ 11:06 AM

    Three thumbs up!

  2. Adam KirkwoodFebruary 01, 2009 @ 11:39 AM

    I can’t wait! This will make my life so much easier. Great job guys!

  3. Michael DeeringFebruary 01, 2009 @ 01:36 PM

    That is a very thorough usage example. Thanks!

    It will be nice to have some solid convention around something I think every one has a home-grown solution to.

  4. Amit KumarFebruary 01, 2009 @ 05:57 PM

    You know what, I was struggling with this just last week where I had 3 models for one form.

    Came like great news !!!

  5. Eloy DuranFebruary 02, 2009 @ 04:25 AM

    Very nice article Ryan, thanks!

    About default behaviour for :reject_if; while I agree with your sentiment, the problem was that it started to get real messy real quick. As you pointed out forms with a checkbox will never have all blank attributes, and then there were some other issues as well.

    Anyways, in such cases I believe in the KISS principle. If I keep it simple enough right now, then people can provide feedback about real situations and ideas that can be extracted from their experience to possibly create a good default and it won’t be bloated with theorized code.

    So if people have good ideas (from experience) on this I’d love to hear from them. I should mention that I prefer code over theory ;)

  6. Ryan DaigleFebruary 02, 2009 @ 08:28 AM

    Thanks for your comments, Eloy. I definitely see your point and think I’ve come around to your side of not having default behavior as I’ve started to integrate this more into my existing apps.

    There seem to be enough exceptions to the “if all are blank” rule that it doesn’t make much sense to include that as the default.

    Thanks so much for this contribution – this is a big win for Rails.

  7. billstickersFebruary 02, 2009 @ 03:05 PM

    fyi, trying to assign

    person.children_attributes = [ { :name => ‘Son’ } ]

    in the console gives me ArgumentError: Hash expected, got Array

    here’s the full error (using project/tasks): http://pastie.org/377613

  8. Eloy DuranFebruary 02, 2009 @ 03:20 PM

    Indeed. I totally forgot we pulled that out, it was a remnant of the early days which didn’t really fit in anymore because it only builds records. So everything goes in as hashes.

    Could it be that you were using an older version? It might be because of all the different branches I have, I should really clean that up sorry :) This is the commit where it got removed: http://github.com/alloy/rails/commit/4c753ad44dc461d7a43d1810464973f2604f9d7f

    @billstickers: Thanks for the reminder :)

  9. Ryan DaigleFebruary 02, 2009 @ 03:40 PM

    @billstickers, you’re right, I’m seeing that too. I’ll update my post.

  10. Lance IvyFebruary 02, 2009 @ 06:26 PM

    I think the only possible good :reject_if option is:

    :reject_if => proc{|attrs| attrs[:_delete] != "1"}

    Any other configuration could possibly reject intentional user input. Even checkboxes, date fields, and booleans could be submitted with default values.

    We have the _delete flag so we can capture explicit user intention and not concern ourselves with logic that checks for missing rows, etc. That’s the only way to always get it right. So why not capture explicit user intention for the creation of new rows as well?

    In my experience, the delete flag is best implemented from the UX perspective as a “keep” checkbox that is by default unchecked for the blank row. That works well because users think better in terms of opt-in (check the box to _keep the record) than opt-out (check the box to destroy the record). The example app in http://github.com/cainlevy/complex-form-examples/tree/cainlevy (note the `cainlevy’ branch) should show this in action using my (now deprecated) nested_assignment plugin.

    So, if that is the only possible good :reject_if configuration, should the :reject_if option disappear altogether so people don’t have the opportunity to get it wrong?

  11. Lance IvyFebruary 02, 2009 @ 06:30 PM

    So of course I got the “only possible right” configuration wrong. :-)

    It should be:

    :reject_if => proc{|attrs| attrs[:_delete] == "1"}

    Would be nice if that worked for {:_delete => true} as well as {:_delete => “1”}, but I’m not sure what the best way is to do that.

  12. billstickersFebruary 03, 2009 @ 11:36 PM

    thanks guys.

    i can’t tell you how awesome this is. i have a very complicated model (a 10+ page data entry application) and this will let me remove more than 200 lines of code from the model.

  13. moFebruary 04, 2009 @ 02:43 PM

    yeah.. that’s really awesome.. but I habe still problems with validation / error messages.

    class Person < ActiveRecord::Base validates_presence_of :name, :message => “no name” end

    new/create

    @person.children_attributes = { ‘new_1’ => { :name => ’’ } } -> error: “child is invalid”

    edit/update

    @person.children_attributes = { ‘1’ => { :name => ’’ } } -> error: “no name”

    Why do I not get the explicit error message on new/create?

  14. SuperslauFebruary 04, 2009 @ 05:57 PM

    This feature is really awesome. I have implemented this with polymorphic associations, and it works!

    class Task < ActiveRecord::Base has_many :assets, :dependent=>:destroy accepts_nested_attributes_for :assets, :allow_destroy => true end

    belongs_to :workable, :polymorphic => true

    class Upload < ActiveRecord::Base has_one :task, :as => :workable, :dependent=>:destroy accepts_nested_attributes_for :task, :allow_destroy => true end

    Upload is a kind of task. All tasks can have one or more assets uploaded.

    I took me a while to figure out that I should use the Upload model as the parent. So in one form, I can create an upload, and it’s corresponding task entry, along with a file upload.

    in my controller:

    def new
      @upload = Upload.new
      @upload.task = Task.new
      @upload.task.assets.build
    end

    Don’t worry if that doesn’t make any sense, I just wanted to let people know that accepts_nested_attributes_for works just fine with polymorphic associations. Thanks Eloy!

  15. Eloy DuranFebruary 05, 2009 @ 04:02 AM

    I asked Lance to create a ticket with his thoughts so it doesn’t get lost. Which he was kind enough to do: http://rails.lighthouseapp.com/projects/8994/tickets/1870

    @mo: Please file a ticket if you encounter a bug, this is not the place for it because I will lose track of it.

    @Superslau: Thank you very much for letting me know this!

  16. Alexander JolovicFebruary 06, 2009 @ 06:39 PM

    This is a great addition to Rials! Thanks guys!

    I have a question with regard to partials. How do you call up a partial with a parent variable being a receiver to fields_for method? I hope I make any sense. In other word, how do you manage partials with multiple local variables?

    Thank you to anyone who can answer this question.

    -Alex

  17. Glenn PowellFebruary 07, 2009 @ 03:48 AM

    I seem to see a couple problems with this new implementation.

    Firstly, when your child (for a 1-to-1) tries to validates_presence_of :parent, you will get an error upon creation because the parent doesn’t have an id yet.

    Secondly, I haven’t been able to get any nested forms to go 3+ levels deep, since in FormBuilder, this method is called:

    nested_attributes_association? @object.respond_to?(”#{association_name}_attributes=”) end

    in order to determines if the FormBuilder should which is being created in response to fields_for should be “nested. But this method will always fail if @object is nil (which it will be when you are creating new records and you are deeper than the top-level record).

    So if a User has_one Contact has_one Address, then the form for the Address will not work since the @object for Contact is nil. I have created a custom form which alleviates this problem (for myself at least). Now I can create forms n-levels deep. But am I missing something in the core implementation?

  18. Eloy DuranFebruary 07, 2009 @ 07:57 AM

    @Glenn Please file a ticket with your needed workaround and notify me of the ticket at: .

    Eloy

  19. Eloy DuranFebruary 07, 2009 @ 07:58 AM

    It seems my comment didn’t make it as I wanted to.

    @Glenn: Please file a ticket with your needed workaround and notify me of the ticket: eloy AT fngtps DOT com

  20. Tamer SalamaFebruary 10, 2009 @ 10:32 PM

    Validating a child instance for the presence of the foreign key creates a circular dependency (the child validation is done before the parent id is propagated to the child).

    
    
    class Person < ActiveRecord::Base
      has_many :children
      accepts_nested_attributes_for :children
    end
    
    class Child < ActiveRecord::Base
      belongs_to :person
    
      #Validating that a toy belongs to a person
      validates_presence_of :person_id
    end
    
      p = Person.new(:name => "Smith", :children_attributes => {"new_1" => {:name => "John"}})
      p.valid? #=> false
      p.save  #false
      p.children.first.errors.on(:person_id) #=> "can't be blank" 
    
    

    Is it just late or does it need a work-around?

  21. Tamer SalamaFebruary 11, 2009 @ 11:56 AM

    FYI – I’ve created a ticket for the validation circular dependency issue comment

    http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1943

  22. David KahnFebruary 11, 2009 @ 09:35 PM

    Is there a reason the validations for children models run only if the parent model passes all validations?

    It seems to me that it would be helpful for users to get feedback on all errors in the form at once, rather than having to correct the parent model before finding out that a child model has errors on it too.

  23. Eloy DuranFebruary 13, 2009 @ 04:51 AM

    @David: There’s already a patch for that specific issue: http://rails.lighthouseapp.com:80/projects/8994/tickets/1930

  24. Gastón RamosFebruary 13, 2009 @ 07:41 AM

    Hi I’m trying Rails 2.3 with nested forms and doesn’t work with polymorphic associations:

    The models:

    class Person < ActiveRecord::Base belongs_to :personable, :polymorphic => true end

    class User < ActiveRecord::Base has_one :person, :as => :personable, :dependent => :destroy accepts_nested_attributes_for :person end

    The form:

    Sign up as a new user

    <% @user.password = @user.password_confirmation = nil %>

    <%= error_messages_for :user %>

    <% form_for :user, :url => users_path do |f| -%>

    <% f.fields_for :person do |person_form| %>

    <%= person_form.label :first_name, ‘First Name:’ %>
    <%= person_form.text_field :first_name %>

    <%= person_form.label :last_name, ‘Last Name:’ %>
    <%= person_form.text_field :last_name %></p

    <% end %>

    <%= label_tag ‘login’ %>
    <%= f.text_field :login %>

    <%= label_tag ‘password’ %>
    <%= f.password_field :password %>

    <%= label_tag ‘password_confirmation’, ‘Confirm Password’ %>
    <%= f.password_field :password_confirmation %>

    <%= submit_tag ‘Sign up’ %>

    <% end -%>

    ActiveRecord::AssociationTypeMismatch (Person(#-619527948) expected, got HashWithIndifferentAccess(#-607513158)):

    app/controllers/users_controller.rb:14:in `new'
    app/controllers/users_controller.rb:14:in `create'
  25. Michał CzyżFebruary 14, 2009 @ 02:23 PM

    i’ve got this same problem, like you Gastón Ramos, when i write something like this:

    - f.fields_for :steps do |step|

    after some combination, i figured out, that adding ‘_attributes’ suffix in form builder helps

    - f.fields_for :steps do |step|

    is this should work like this ?

  26. Michał CzyżFebruary 14, 2009 @ 02:28 PM

    i’m made mistake in second line from down, it should be

    - f.fields_for :steps_attributes do |step|

  27. Gastón RamosFebruary 18, 2009 @ 07:38 AM

    Yes, Michal this work but now I get a problem on edit form where person attributes are empty :/

  28. valkFebruary 26, 2009 @ 08:36 PM

    Hi Ryan & thanks for this fantastic article!

    Sorry, but I’m having a trouble with accepts_nested_attributes_for. Rails screams: .. undefined method `accepts_nested_attributes_for’ for # .. /usr/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:1833:in `method_missing’ /home/val/NetBeansProjects/lena_cooks/app/models/post.rb:4 /home/val/NetBeansProjects/lena_cooks/app/controllers/posts_controller.rb:14:in `index’

    I have rails 2.3. Does it look familiar?

    I posted a question at http://railsforum.com/viewtopic.php?pid=89048#p89048.

  29. NickFebruary 27, 2009 @ 09:05 PM

    I’m trying to use these nested forms in a project and ive noticed a problem with updating..

    Say im editing a person and his children all on the same page, and in that page i want to be able to also add new children (say i forgot someone). When i resubmit the form it doesnt recognize the ID and throws

    The error occurred while evaluating nil.attributes=): activerecord (2.3.0) lib/active_record/nested_attributes.rb:275:in `assign_to_or_destroy_nested_attributes_record’ activerecord (2.3.0) lib/active_record/nested_attributes.rb:243:in `assign_nested_attributes_for_collection_association’ activerecord (2.3.0) lib/active_record/nested_attributes.rb:239:in `each’ activerecord (2.3.0) lib/active_record/nested_attributes.rb:239:in `assign_nested_attributes_for_collection_association’ activerecord (2.3.0) lib/active_record/nested_attributes.rb:189:in `tracks_attributes=’

    what does the ‘id’ need to be for it to recognize that im adding a new guy and not updating someone alongside the ones that i am updating?

  30. valkFebruary 28, 2009 @ 03:53 PM

    Sorry for repeating Alexander Jolovic, But any idea of how to use it with partials? Thanks!!

  31. Hubert ŁępickiMarch 02, 2009 @ 12:08 PM

    This looks nice indeed!

    But this solution is not completely new. Of course, it was standardised and included in Rails by default, which is great.

    But solution to nested forms using virtual attributes was widely used years before.

    However, it’s very good that it was included into Rails, just because it provides standard that most developers will use now. Previously every body were defining nested attributes different way, and there is not really a rocket science to it, this should just work.

    Nice, restful way of building forms :).

  32. Sébastien Grosjean - ZenCocoonMarch 02, 2009 @ 07:22 PM

    Really nice work, such a pleasure to use !

    Just got a question regarding Eloy demo ” http://github.com/alloy/complex-form-examples/blob/92b31b6205f7f9e51578b0608ca27677a88a3ee3/app/helpers/projects_helper.rb “

    Is anyone has come up with an unobtrusive method already ?

    Thanks again Eloy for this wonderful contribution and Ryan for this perfect tutorial, really clear and makes on track in couple minutes !

  33. Ian DrysdaleMarch 03, 2009 @ 07:31 PM

    Glenn, I’m struggling with a form 3 layers deep, too. Please can you share your custom form you’ve created to deal with this? You can reach me at idrysdale AT gmail DOT com

  34. Glenn PowellMarch 05, 2009 @ 12:40 PM

    For Ian et al, You can see my custom form in my framework plugin: http://github.com/glennpow/trainyard/blob/b2a198feb4feec7610c0777367e3563a9973e067/lib/trainyard/content_form_builder.rb

    The relevant parts are the initialize and form_for methods.

  35. Alexander JolovicMarch 06, 2009 @ 06:17 PM

    Can anyone address the issue of using partials? Thanks

  36. John SmallMarch 10, 2009 @ 06:24 AM

    Shouldn’t <%= child_form.checkbox ‘_delete’ %> be <%= child_form.check_box ‘_delete’ %> ?

    The first one fails, the second one works and the rails api docs refer to check_box not checkbox.

  37. Ryan DaigleMarch 10, 2009 @ 07:33 AM

    John – thanks for the catch, I’ve updated the example to use check_box

  38. Ian DrysdaleMarch 10, 2009 @ 07:02 PM

    How can this work with a :file_field? It works fine when creating a new model, however when editing one that has a number of existing files associated, these fields are displayed as empty, and fails on save (or deletes the files). Am I missing a trick?

  39. Caroline WrightMarch 15, 2009 @ 11:02 PM

    I’m using a helper to dynamically add a new child form, which is a partial. When I do ‘parent_form.fields for’ I get an error. Any idea of how to get around this?

    I’m pretty new to rails so I may be missing something.

  40. Glenn PowellMarch 17, 2009 @ 06:18 AM

    Caroline, I’m assuming you meant: ‘parent_form.fields_for’ (with underscore). Can you post your error?

  41. L SMarch 17, 2009 @ 01:20 PM

    Hi,

    I am trying to write view tests for this new feature, but I am having problems. Has anybody written a rspec test for this?

    Can you show me an example?

    Thanks!

  42. ClaytonMarch 19, 2009 @ 02:35 AM

    I really like this new feature, but I’ve got the same problem as some others when using partials.

    I have a form that has two levels of nesting, the inner most form is in a partial that I’d like to be able to duplicate via an AJAX call. Unfortunately I don’t know of anyway to pass a locals hash with the necessary FormBuilders through the AJAX request.

    Looks like I might need to go back to the “old” way of doing nested forms. :(

  43. GrahamMarch 19, 2009 @ 11:46 AM

    For the delete thing to work you now need (2.3.2) to add :allow_destroy => true to accepts_nested_attributes_for. Also inside your :reject_if proc you have to access the attributes with a string, symbols don’t work.

  44. SvenMarch 27, 2009 @ 03:57 AM

    I found something which might help about the partials. Just pass form to the locals variable, i.e.:

    = render :partial => ‘some_partial’, :locals => { :pf => f }

    and access it in the partial:

    - pf.fields_for :some_model do |model_form|

    I hope this helps.

  45. Sebastian CamposMarch 31, 2009 @ 02:06 PM

    Can someone point me to an example of this working with polymorphic associations?

    Danke.

  46. Josh WhiteApril 03, 2009 @ 06:12 PM

    I am trying to get the nested attributes for working but has_many seems to be giving me problems.

    In short …

    FindingData has many :finding_measurement_datas accepts_nested_attributes_for :finding_measurement_datas

    I know my class is set up correctly and my keys are all linked fine, and my controller pretty much just runs

    FindingData.first.update_attributes(params[:finding_data])

    Here are the params from the log

    Processing ReadingsController#update (for xx.xxx.xxx.xx at 2009-04-03 17:06:00) [POST] Parameters: {“id”=>”97”, “finding_datas”=>[{“name”=>”Mammo Mass 1”, “finding_measurement_datas”=>[{“finding_measurement_id”=>”15”, “id”=>”1”, “measurement”=>”1.34”, “finding_data_id”=>”1”}, {“finding_measurement_id”=>”19”, “id”=>”2”, “measurement”=>”test.1”, “finding_data_id”=>”1”}]}}

    Here is the error ActiveRecord::AssociationTypeMismatch (FindingMeasurementData(#-609265458) expected, got HashWithIndifferentAccess(#-605880508)): app/controllers/readings_controller.rb:193:in `update’

    I don’t get why it can’t just iterate all the nested finding_measurement_datas ? Am I misunderstanding what this patch is supposed to be doing?

  47. John DavosApril 03, 2009 @ 08:13 PM

    Is anyone having an issue regarding rendering fields on failed validation?

    I have a user and company model where a company has many users. When someone creates a company they must also have a user account, so for that I am using:

    <- f.fields_for :users do |u| -> <= u.error_messages -> <%= render :partial => “users/form”, :locals => {:f => u} %> <- end ->

    To build the objects I am using the same helper Ryan recommends above. However, when testing this in the browser using an existing email (emails must be unique) I receive the error message “Users email has already been taken” but the fields are gone. Am I doing something wrong? Is something else needed to build the object when it fails validation?

    Thanks

  48. JoshApril 05, 2009 @ 12:31 AM

    I figured out the problem

    In my hash below

    “exam_record”=>{“name”=>”exam_record”, “finding_datas”=> [{“name”=>”Mammo Mass 1”, “updated_at”=>”2009-03-27T21:54:24-05:00”, “finding_type_id”=>”15”, “selected”=>”1”, “id”=>”1”, “finding_measurement_datas”=> [{“finding_measurement_id”=>”15”, “id”=>”1”, “measurement”=>”1.34”, finding_data_id”=>”1”}, {“finding_measurement_id”=>”19”, “id”=>”2”, “measurement”=>”1”, “finding_data_id”=>”1”}], }}

    I needed the hash key for the finding_measurement_datas to be called finding_measurement_datas_attributes, so it would look like this “exam_record”=>{“name”=>”exam_record”, “finding_datas”=> [{“name”=>”Mammo Mass 1”, “updated_at”=>”2009-03-27T21:54:24-05:00”, “finding_type_id”=>”15”, “selected”=>”1”, “id”=>”1”, “finding_measurement_datas_attributes”=> [{“finding_measurement_id”=>”15”, “id”=>”1”, “measurement”=>”1.34”, finding_data_id”=>”1”}, {“finding_measurement_id”=>”19”, “id”=>”2”, “measurement”=>”1”, “finding_data_id”=>”1”}], }}

    If I were using rails views and not flex xml I wouldn’t have had the problem so its not a bug, just that the params converstion is not well documented.

  49. Matt WebsterApril 07, 2009 @ 03:42 PM

    how important is ”:class_name => ‘Person’” in the model’s has_many :children, :class_name => ‘Person’

    I ask because with it, I get internal SQLite errors and without it, I work: (here’s the “not working” result) NoMethodError Exception: undefined method `case_info’ for # (rdb:3) p patient.case_infos INTERNAL ERROR!!! SQLite3::SQLException: no such column: patients.patient_id: SELECT * FROM “patients” WHERE (“patients”.patient_id = 295) /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/abstract_adapter.rb:212:in `log’ /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/sqlite_adapter.rb:157:in `execute’ ... lots more … /usr/lib/ruby/gems/1.8/gems/rails-2.3.2/lib/commands/server.rb:111 /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require’ /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’ ./script/server:3/home/mwebster/projects/naf/hotline/app/views/patients/index.html.erb:28

    (the beginning of my ‘not working’ model) class Patient < ActiveRecord::Base has_many :case_infos, :class_name => ‘Patient’ accepts_nested_attributes_for :case_infos, :allow_destroy => true

    (the beginning of my routes… although this occurs in development w/o running ‘rake routes’) ActionController::Routing::Routes.draw do |map| map.resources :calls map.resources :patients, :has_many => :case_infos map.resources :case_infos

    For now, I’m leaving out the :class_name callout in the model and, so far, I’m ok.

  50. Ben TuckerApril 07, 2009 @ 06:03 PM

    @John Davos, I think I ran into a similar issue. I wrote up my solution here: http://stream.btucker.org/post/93887650/accepts-nested-attributes-for-fields-for-attr-accessible

  51. TeoulasApril 08, 2009 @ 12:14 PM

    With regards to partials in nested resources I ended up calling the partial with an :object parameter like this (I’m using haml, but it should be easy to adapt it to your template language):

    The view: -form_for :parent_model do |parent| =parent.text_field :name # put your parent_model fields here -parent.fields_for :children do |child| =render :partial => ‘child’, :object => child

    and in the _child partial you access the child object as usual.

    Hope this helps!

  52. FreyaApril 10, 2009 @ 08:04 PM

    I am trying to get double nested attributes to work and haven’t had any luck:

    class Event < ActiveRecord::Base has_many :topics accepts_nested_attributes_for :speakers end

    class Topic < ActiveRecord::Base belongs_to :event has_many :speakers accepts_nested_attributes_for :speakers end

    class Speaker < ActiveRecord::Base belongs_to :topic end

    In my Event Controller:

    def new
      @event = Event.new
      @event.topics.build
    end

    In my I have a the standard f.fields_for :topics do |topic| which works.

    Then to get to the speakers I have topic.fields_for :speakers do |speaker| which doesn’t work. Has anyone else been able to get this working?

  53. Steve AbramsApril 24, 2009 @ 12:56 AM

    First, thank you very much for this article, very informative!

    I’m fairly new to Rails and Ruby, and am working with nested forms. I see the deletion method of nested models to be less-than-ideal: this seems like functionality that should be in the domain (model) rather than the controllers. Wouldn’t it be cleaner to override the destroy method in the parent model (and subsequent models if nesting is deeper than one extra level), so that this logic is executed in every deletion of the parent model?

    Thanks for any input! Steve

  54. Steve AbramsApril 24, 2009 @ 01:04 AM

    Freya,

    I think instead of

    class Event < ActiveRecord::Base has_many :topics accepts_nested_attributes_for :speakers end

    you want

    class Event < ActiveRecord::Base has_many :topics accepts_nested_attributes_for :topics end