#196
Jan 11, 2010

Nested Model Form Part 1

Handling multiple models in a single form is much easier with the accepts_nested_attributes_for method. See how to use this method to handle nested model fields.
Download (22.4 MB, 11:09)
alternative download for iPod & Apple TV (15.2 MB, 11:09)

Resources

rails surveysays
script/generate nifty_layout
script/generate nifty_scaffold survey name:string
script/generate model question survey_id:integer content:text
script/generate model answer question_id:integer content:string
rake db:migrate
# models/survey.rb
class Survey < ActiveRecord::Base
  has_many :questions, :dependent => :destroy
  accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end

# models/question.rb
class Question < ActiveRecord::Base
  belongs_to :survey
  has_many :answers, :dependent => :destroy
  accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end

# models/answer.rb
class Answer < ActiveRecord::Base
  belongs_to :question
end

# surveys_controller.rb
def new
  @survey = Survey.new
  3.times do
    question = @survey.questions.build
    4.times { question.answers.build }
  end
end
<!-- views/surveys/_form.html.erb -->
<% form_for @survey do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <% f.fields_for :questions do |builder| %>
    <%= render "question_fields", :f => builder %>
  <% end %>
  <p><%= f.submit "Submit" %></p>
<% end %>

<!-- views/surveys/_question_fields.html.erb -->
<p>
  <%= f.label :content, "Question" %><br />
  <%= f.text_area :content, :rows => 3 %><br />
  <%= f.check_box :_destroy %>
  <%= f.label :_destroy, "Remove Question" %>
</p>
<% f.fields_for :answers do |builder| %>
  <%= render 'answer_fields', :f => builder %>
<% end %>

<!-- views/surveys/_answer_fields.html.erb -->
<p>
  <%= f.label :content, "Answer" %>
  <%= f.text_field :content %>
  <%= f.check_box :_destroy %>
  <%= f.label :_destroy, "Remove" %>
</p>

RSS Feed for Episode Comments 94 comments

1. Ryan Jan 11, 2010 at 00:01

Thank you! Your work is always appreciated!


2. Lee Jan 11, 2010 at 00:18

another brilliant tutorial :) thanks Ryan


3. David Jan 11, 2010 at 00:30

Nice. :)


4. Jamie, Baymard Institute Jan 11, 2010 at 00:30

Part 1.. like the sound of that.

Many thanks as usual..


5. Juanma Jan 11, 2010 at 00:31

Thanks Ryan. Very goog job!
Can I suggest another screencast related to this one?
I really appreciate something like this with MongoMapper?
The surveys sample fits very well with the concept of a document database.
Is it possible?
Thank you very much.


6. Stuart Jan 11, 2010 at 01:28

I've heard this can work somewhat for has many through, but I've not seen a concrete example. Is that what part 2 might be?


7. Andrew Jan 11, 2010 at 01:28

Thanks for this Ryan, looking forward to this series.

As it happens I am near completion of a survey website in which I am making use of nested model forms.

I came across a painful bug in the version of rails which I am using (2.3.4):
Check boxes used for data entry (e.g. a boolean value in my model) cause the hashes in params to be split for nested models when the user ticks them.

For example I have a form which includes a nested model called "CourseOffering".
When the user does not tick any check boxes the params hash includes a course_offerings_attributes hash which itself contains one hash for each course offering (as you would expect).
When they do tick a check box, that particular course offering gets split into two different hashes, this causes various problems depending on validation rules.

The only way I was able to overcome this problems was to use select boxes instead.

I hope I have explained this clearly enough - I think it's something to watch out for!


8. Jayoshi Jan 11, 2010 at 01:40

Great screencast, I look forward to the next episode!


9. Steve Jan 11, 2010 at 01:58

Thanks for another quality screencast! Your effort is always appreciated!


10. jofr Jan 11, 2010 at 02:27

We are looking forward to see part 2


11. Fredrik Jan 11, 2010 at 02:29

Nested model forms are really helpful, I use them right now to add some generic attributes to different models with a has_one relationship. I am wonder how to add these javascript add / remove links though, it seemed not so easy last time I searched for a solution.


12. Olli Jan 11, 2010 at 02:36

Thank you. I've learn a lot with your casts :)


13. Gethryn Jan 11, 2010 at 02:48

Really happy to see updated screencasts for nested forms. Thanks for all your hard work.


14. Joost Saanen Jan 11, 2010 at 03:01

Just luv nested model forms, great cast. Looking forward to see the next parts!


15. Chris Lerum Jan 11, 2010 at 03:06

using latest nifty_generators, in the survey model i see the following, but it's not in the railscast:

attr_accessible :name

unless i comment this out, cannot edit questions/answers


16. Brandon Jan 11, 2010 at 03:58

Does accepts_nested_attributes_for work well with virtual attributes?


17. Michael Jan 11, 2010 at 04:22

As always, excellent Ryan. I've been a heavy user of the complex forms method, this looks way smarter.


18. StuFF mc Jan 11, 2010 at 04:29

Thanks Ryan! Can't wait Part 2 ;) BTW what is the TextMate plugin you use for creating partials? I can't find one working. Same thing with the one putting links to TextMate when an error occurs, couldn't make it work (this is a Rails plugin, not a TM).


19. Randy Jan 11, 2010 at 05:41

Awesome timing. I just had to do this in a project and wasn't 100% sure I was doing it properly. This just confirmed it for me!

Thanks!


20. Paddy Jan 11, 2010 at 05:53

Hello Ryan,

Thank you for yet another awesome cast!

BTW, how are you filling the forms with a touch/click? Which plugin/tool are you using for that?

Thank you!

Paddy


21. Anlek Jan 11, 2010 at 06:38

Great stuff Ryan!
Try looking into LowPro for Prototype to create a nested behavior JS class (andd/remove links). It makes your JS code very reusable (http://bit.ly/76dDzz) and there is a version of LowPro for jQuery. And if anyone cares, RightJS comes with most of the functionality built in ;)


22. Steve Jan 11, 2010 at 06:43

Superb. You have raised the screencast quality bar to a new level.
I really love how your increasing mastery of the art is expressed by the emergence of a subtle humor: the finger snap, the answer to the universe... I follow your casts to learn, obviously, but beyond pure information I find them enjoyable because of the sense of being spoken to directly. I'm so impressed.


23. Tim S Jan 11, 2010 at 06:55

It's amazing how often these screencasts come out just as I'm trying to figure out how something works.

Specifically, the reject_if was something I was wondering how to implement for today. Thanks for saving me a few hours! :-)


24. Matt R Jan 11, 2010 at 07:06

lambda? what was that?


25. Luis Jan 11, 2010 at 07:49

Would it be possible in a future show to also give an example of how to ask questions that are dependent on the answer. Like: if answer == 2
then ask these 4 questions, etc.?

Thanks.
Great Screencasts!!!!

-Luis.


26. Benjamin Manns Jan 11, 2010 at 08:51

You're a day late with this - I spent hours yesterday trying to figure this out! I really look forward to part two.


27. John Descy Jan 11, 2010 at 10:05

Something wrong with cancan? Your "Report as Spam" links don't work anymore and opening them in a new window gives me a "Not authorized to access this page." message.

If I may suggest a cast topic: could you do a cast on testing models with associations and sessions? I find this really troublesome. My app works, but a lot of tests fail, because some objects turn out to be nil. I have nailed this down to sessions not working so well. I have found a snippet of yours in some forum, but this doesn't help. Well, it did help, but sometimes it doesn't. Very strange...

Last, but not least: Thanks for all those great screencasts. :-)


28. Michael Janssen Jan 11, 2010 at 10:15

This is a great update to one of my most-referenced railscasts! So glad to see the multiple nesting and how much easier it has become in Rails 2.3!


29. Eric Richmond Jan 11, 2010 at 14:48

Does anyone know if formtastic supports nested models?


30. lelanhus Jan 11, 2010 at 16:26

Yes, formtastic supports nested models.

It's in the documentation.

http://github.com/justinfrench/formtastic


31. Eric Richmond Jan 11, 2010 at 17:33

Interesting. I was unable to get this functionality working in formtastic.

I'll try again, mimicing Ryan's code.

Thanks!


32. Micah Winkelspecht Jan 11, 2010 at 18:07

Exactly what I needed, exactly when I needed it. Thanks, Ryan. Looking forward to Part Deux.


33. Daryl Jan 11, 2010 at 18:13

Will you do a railscast about the plugin you use for the snapping of the fingers? :)

P.S. This cast is my favourite! Can't wait until next week.


34. 640-802 Jan 11, 2010 at 18:30

Interesting.
This is a great update to one of my most-referenced railscasts!


35. Boris Jan 11, 2010 at 18:58

Thank you Ryan for another series of screencasts!

Hopefully this time you will cover long-awaited validation in deeply nested forms.

Thanks a MILLION,
Boris


36. Branden Jan 11, 2010 at 21:53

You are amazing Ryan! I was just bashing my head in the other day wondering how to pull this off with my controllers spazzing out at one another. Not the best task for a newb.

It's trippy how you always seem to find what I need at the right time.

Thanks


37. Nicolás Hock Jan 11, 2010 at 22:59

I guess it's not _destroy but _delete. The _destroy was not working for me and I looked at railsbrain.com and it was actually _delete.

http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M002182&name=accepts_nested_attributes_for


38. Artur Jan 12, 2010 at 02:11

really nice tutorial, looking forward for part two. Also interested in deleting and sorting elements using js


39. Nami Jan 12, 2010 at 02:35

For who search for "remove", it's a js code as (jQuery):[javascript]var jQuery( '.remove' ).live( 'click', function ()
{
  jQuery( this ).find( '.checkbox_remove' ).attr( 'checked', true );
} );[/javascript](you need to just add the class 'remove' to link and 'checkbox_remove' to the checkbox "remove"


40. Juan Jan 12, 2010 at 06:13

Thanks Ryan! Great tutorial, as I was struggling in a project with many nested models.


41. Steve Jan 12, 2010 at 08:11

For some reason, I can't get this to work:

:reject_if => lambda { |a| a[:content].blank? }

It simply won't save the questions to the database. Anyone else having that issue?

If I remove it, the questions are saved but blank ones are saved as well - just as in the screencast prior to adding that line to Ryan's controller.


42. Steve Jan 12, 2010 at 08:14

Ah, it seems to be an issue with passing in the content as a symbol. If I do it like this it works fine:

:reject_if => lambda { |a| a['content'].blank? }

Can anyone help me understand why?


43. Florian Jan 12, 2010 at 09:11

@Chris Lerum you need to do give the nested_attribute free:
attr_accessible :questions_attributes


44. David Jan 12, 2010 at 11:48

Great screencast, Ryan. I found myself using the Complex Form series quite a lot in my work. I am loving passing these as attributes so easily.

I did wonder about a TextMate-ism in your railscast. I noticed once you made your partial you were able to start at "builder." and select up to builder on multiple lines and then used a key combo to select only "builder" in order to change all instances to "f". Is that a custom item in a bundle or is it available and I just don't know about it?

I did notice some updates already with Rails 2.3.5.

1. _destroy has been changed to _delete
2. a[:content] is not recognizing the string representation of content, so it must be a['content']

I am loving the new changes and can't wait till Part 2!


45. Lukas Jan 12, 2010 at 13:02

Yet another fantastic railscast, I am looking forward to the follow up - thank you Ryan!


46. Lyle Jan 12, 2010 at 14:31

Ryan, when you work on the second part of this tutorial, add a belongs_to relationship to Survey. For example: author. In survey.rb add accepts_nested_attributes_for :author. Incorporate this into your form.

I mention this because nested forms doesn't handle belongs_to relationships properly. If you want to change the author for a single survey, it will do something like:

update authors set name = 'bar' where id = 1

Which affects all surveys that point to that author.

What it should do is:

insert into authors (id, name) values (2, 'bar')
update surveys set author_id = 2 where id = 5


47. Yinghai Jan 12, 2010 at 22:23

Thank you very much for your work.
Is there a way to integrate accepts_nested_attributes_for and formtastic?

looing forward to see your part two


48. godweed Jan 13, 2010 at 03:11

Thanks for the screencast, hoping part 2 will be has many through example of the nested forms.


49. vladimir prieto Jan 13, 2010 at 04:31

this is one of my favorite and helpfull episodes.

anxiously watting the part 2.


50. Eric Richmond Jan 13, 2010 at 05:34

I asked the same question Yinghai, someone claimed you could, but I've not yet see it work.


51. Branden Jan 13, 2010 at 07:54

Ryan, I'm with Lyle. Is there any way you can get this on the opposite end of the relationship on the belongs_to? It should work is what I've seen on the internet but I keep getting errors.

Also, does it work with anything other than a has_many with belongs_to relationship?


52. Özgün Koyun Jan 13, 2010 at 07:57

_delete vs _destroy:

DEPRECATION WARNING: _delete is deprecated in nested attributes. Use _destroy instead.. (called from _delete at /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/nested_attributes.rb:263)

Thanks!


53. DanS Jan 13, 2010 at 08:45

StuFF mc, Ryan is using the Textmate "Ruby on Rails" bundle to create the partials. This is available with "control shift H" or from the bundle menu "Ruby on Rails > View Templates > Create partial from selection"


54. DanS Jan 13, 2010 at 09:02

David, Ryan is using Textmate's column edit to quickly change the "builder." to "f." To use this push and hold the option button, your Textmate cursor changes to a cross, left click and drag the cross to select the column of text you want to change. Once you have a selection just type the new text you want.


55. yanosz Jan 13, 2010 at 12:02

Nice work Ryan - Thanks for picking up complex forms again.

Btw. is there a simple way to associate already created questions with a new survey?
Can a survey contain questions that are already part of other surveys?

Thanks
yanosz


56. Darren Jan 13, 2010 at 15:03

@steve I had the same issue. I think Ryan's setup must have magic in it cause the api says to do it the way you did and not with :symbol.


57. Tom Jan 13, 2010 at 18:15

Another great screencast and glad you're updating your Complex Forms series. That's been one of my favorites, but

@ryan, or anyone for that matter, do you think you'll ever address how to get the same model multiple times in the one form?


58. Miriam Jan 13, 2010 at 22:13

Great screen cast, just what I need right now!
I'm trying to understand the :reject_if => lambda { |a| a[:content].blank? }
I have forms with multiple inputs and checking for blank inputs wants all of them filled out. For example, if instead of "Question" as the input it had "First Name", "Middle Name" and "Last Name". How can you check that the first name is not blank but have it be optional whether to fill out the middle name? Which levels of the nesting attributes have to have no blanks to validate?

Thanks!


59. Bren Jan 13, 2010 at 23:35

I love you and need part two immediately.


60. Geoff Jan 14, 2010 at 08:12

I didn't read all of the comments but I just ran into something on rails 2.3.2, when I use the

:reject_if => lambda{|a| a[:content].blank? }

it was rejecting everything, I needed to change it to

:reject_if => lambda{|a| a['content'].blank? }

Not sure if it was something silly I was doing or not but worth mentioning if it saves someone some time.


61. Jon Jan 14, 2010 at 15:13

Looking forward to Parts 2 and 3!


62. Jeremiah Jan 15, 2010 at 07:12

Ryan - Another great episode!!! I greatly enjoy and appreciate your casts!


63. Alexis Jan 15, 2010 at 18:25

As always, thanks for the great cast.

I was wondering if there was a way to access the underlying model from within a form builder, to use data from the db to dynamically create things like the "labels" for the form.

So let's say you have the following form: http://pastie.org/780413

Is there some way that I can get data from the db for instance for the label. So that in stead of "Question", I can do something like question(:name) or question.name or question[name] or whatever it would be. I've tried a lot of different things.

I'd be using it in a different context, so that would explain why it may not make much sense in the current example.

I'd appreciate any help.

-A.


64. Donald Jan 15, 2010 at 19:46

Wow, I was just about to start a project with nested forms and was looking around for sample code.

Great work.


65. Sergei Jan 16, 2010 at 02:18

Does anyone know how can we use this method with Paperclip plugin?
I mean, when we have Survey model and Photo model for example.
Every Survey has many Photos, i.e. Survey has multiple attachments.

Thanks in advance!


66. Fredrik Jan 16, 2010 at 02:33

@Sergei

Look into polymorphic Paperclip attachments :)!


67. Joe Jan 16, 2010 at 12:24

Thank you sooo much for this. Rescued me from a 3 hour head banging session


68. Dimas Cyriaco Jan 16, 2010 at 14:17

beautiful.


69. Ryan Bates Jan 16, 2010 at 16:29

Sorry I can't get to everyone's question, but for those having trouble with the "a[:content.blank?" and "_destroy" behavior working, make sure you're using the latest version of Rails (2.3.5). This behavior has changed recently, and even some of the documentation is out of date.


70. JPablo Jan 17, 2010 at 19:12

Ryan - you are my savior -

needed a solution like this for some time now. But i still have a question

How can i build a survey with many questions and many answers but all user see the same questions and can have only one answer per question. Only the admin can see and edit multiple answers and questions. Your help is very much appreciated


71. Artur Jan 18, 2010 at 07:05

What if I need in partial views/surveys/_answer_fields.html.erb access to some answer attribute, for example answer.id, just use there f.object.id ? Not very nice solution, anyone has better one ?


72. 640-802 Jan 18, 2010 at 23:25

Thank you for your wonderful article, I spent the lunch break.


73. David Jan 19, 2010 at 05:39

Thanks, DanS! I love it! I never even knew to try that! (Re: TextMate's column edit)


74. Ehud Jan 20, 2010 at 07:23

I'm implementing the Remove checkbox option. When the user checks the first of the nested records for removal and I perform a page.reload, the line is removed; however, the first checkbox of the remaining lines is checked. This only occurs in Firefox. It's fine in Safari and Chrome. Does anyone have any ideas on how to fix this problem?


75. hadiS78 Jan 23, 2010 at 04:52

can anybody help me. i always get
unknown attribute: _destroy when trying to delete a record.


76. Manolo Blahnik Jan 24, 2010 at 22:52

Thanks, DanS! I love it! I never even knew to try that! (Re: TextMate's column edit)


77. Sijo Jan 25, 2010 at 02:53

Hi Ryan
    Thanks for this screencast. I am pointing out another general issue here. I am validating html at
http://validator.w3.org/check
   I just added validates_presence_of :name in survey model and without a name when i submit as usual i get the page with error list And when I validates its source I got some error That is exactly as

document type does not allow element "div" here; missing one of "object", "ins", "del", "map", "button" start-tag

    <div class="fieldWithErrors"><label for="survey_name">Name</label></div><b.....

  Why this happens?Is it any issue?

Thanks again
sijo


78. mxtthias Jan 25, 2010 at 06:21

I can only get this to work with projects *created* using the latest version of Rails. The questions are never inserted into the database. It doesn't seem to matter that I have upgraded Rails. Does anyone know what I need to change in a project created with an older version to get this to work?


79. tibet travel Jan 27, 2010 at 00:00

questions are never inserted into the database. It doesn't seem to matter that I have upgraded Rails. Does anyone know what I need to change in a project created with an older version to get this to work?


80. uggs online Jan 29, 2010 at 21:28

Interesting thing!
This is my first time comment at your blog.
Good recommended website.


81. Steve Feb 02, 2010 at 12:37

What if you always want to start with at least one question? Let's say a user submits a survey with no questions, but then wants to add a question when editing the survey?

I know this can be done via javascript (see part 2), but how would I do this without javascript?

I'm basically looking to get the edit view to populate with at least one question field even if that question doesn't exist, and when update_attributes is called to add the new question that was entered.

At the moment, I can do:

If @survey.questions.empty?
@survey.questions.build
end

In my edit method, but the update method is not saving the new question added when editing a survey.

Help!


82. Erdenee Feb 09, 2010 at 01:34

Wow., Thank you very much


83. jai Feb 14, 2010 at 17:52

Thanks for the screencast Ryan :-). I also have validations set up, however, on error the child model errors are displayed first, instead of the parent's. Since the parent fields are the first to show up, those errors should be displayed first. any ideas on what I could be doing wrong? Thanks again.


84. seba Feb 18, 2010 at 17:23

Awesome! Thanks Ryan!


85. Zhaojl Feb 23, 2010 at 04:35

Very Good!
I am work in Windows + Rails 2.3.4,have some problems.
a. proc {|a| ..} replace lambda {|a|..}
b. <%= f.check_box :_delete %> replace <%= f.check_box :_destroy %>


86. darkfall gold Feb 28, 2010 at 18:16

useful,thank you.you guys are kindness.


87. abercrombie clothes Mar 02, 2010 at 23:07

David, Ryan is using Textmate's column edit to quickly change the "builder." to "f." To use this push and hold the option button, your Textmate cursor changes to a cross, left click and drag the cross to select the column of text you want to change. Once you have a selection just type the new text you want.


88. kevin.ho Mar 03, 2010 at 22:42

this is an awesome tutorial. But when i try it out, i encounter some problem.

It simply won't save the questions and survey name to the database after i added field_for in my view. Anyone else having that issue?

If I remove it, survey name are saved


89. Alisha Mar 06, 2010 at 05:04

This is going to sound kind of dumb--I'm sure I just have a missing bracket or something--but when I follow along, I can't get anything at all to render after the fields_for call.
In the following example code, TEST 1 shows up, but TEST 2 and TEST 3 do not--and neither does the partial. The next thing is the submit button.
I would appreciate any help!
Here is my code:

  <p>
    <%= f.label :birthdate %><br />
    <%= f.date_select :birthdate %>
  </p>
  <p>
  TEST 1
    <% f.fields_for :roles do |builder| %>
    TEST 2
      <%= render "_role_fields", :f => builder %>
      TEST 3
    <% end %>
  </p>
  <p><%= f.submit 'Create Actor' %></p>
<% end %>


90. Alisha Mar 07, 2010 at 12:18

Okay, I figured it out, but it doesn't make any sense to me.

In my code, an actor has many roles, so I wrote <%= f.fields_for :roles do |builder| %> and that didn't work.

Changing it to <%= f.fields_for :role %> works. Maybe this is because at the beginning a New Actor has no roles? Is 0 singular?

It works, so I guess I don't care, but I'd sure like to know why, if anyone gets a minute. Thanks.


91. Alisha Mar 07, 2010 at 12:21

Yep. When I added the pre-building 3 roles to my New action, :roles worked. Sorry, I'm a newb! Thanks for the Railscasts, Ryan--they're a great tool.


92. Robert Head Mar 08, 2010 at 09:57

Thank you, Nicolás Hock!

In Rails 2.3.4
builder.check_box :_destroy
should be
builder.check_box :_delete


93. kamagra Mar 09, 2010 at 01:23

thanks for the share


94. m65 field jacket Mar 09, 2010 at 01:23

nice post

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source