Nested Attributes

The regular disclaimer applies to this post: I’m not a rails expert, so I may not be presenting the “best practices” technique, or a technique that will work for everyone all the time. This works for me, in my use case, and I don’t see any obvious reasons why it wouldn’t work for others.

I have two models I’m working with right now in my project, a collection and a video. A collection is essential a bundle of similar videos that share a title, description, date, etc. This supports the concept multiple formats or slight variations of the same video being used in the system. Its a pretty standard setup in my models:

==Collection.rb==

class Collection < ActiveRecord::Base
has_many :videos
end

==Video.rb==

class Video < ActiveRecord::Base
belongs_to :collection
end

So every entry in the video table has an id in the collection_id field that enables that join to take place. Now its time to get a form that will create a collection and the first video in the collection at the same time… initially I spend time trying to generate a text field (using the text_field form helper) that would be called collection[uploaded_data] {uploaded_data is the field I use on my new video form} but I just couldn’t get that to work. I can’t remember where that naming syntax is used.. maybe its CakePHP, Concerto, or even a different rails technique.. but it wasn’t working.

Googling around for “nested attributes” seem to yield some interesting results, and I stubled onto the accepts_nested_attributes_for concept. I changed my collection model to something like this:

class Collection < ActiveRecord::Base
has_many :videos
accepts_nested_attributes_for :videos
end

Now, I had to add a line (@collection.videos.build) to my collections controller so that it will create a new video object:

def new
@collection = Collection.new
@collection.videos.build

respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @collection }
end
end

Last, but not least, I updated updated my form with the following stuff:

<% form_for (@collection,:html => { :multipart => true }) do |f| -%>
<%= f.error_messages %>
<p>
<%= f.label :title %>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %>
<%= f.text_area :description %>
</p>
<p>
<% f.fields_for :videos do |video_fields| %>
<%= video_fields.label :uploaded_data %>
<%= video_fields.file_field :uploaded_data %>
<% end %>
</p>
<p>
<%= f.submit ‘Save’ %>
</p>
<% end -%>

Now the save stuff is all handled by the exact same save I used for the regular collection entry, no need to updated that. Whew!

Here are some additional resources I found helpful:

Leave a Reply

Your email address will not be published. Required fields are marked *