Wednesday, March 26, 2008

params[:fu] #4 ) Use the magical <association_name>_ids=([...array of ids]) association proxy method.

In the last blog entry, we looked at an example of having to build a new parent model and build its join models all in two lines of code. But if you are trying to just assign a list of has_many or has_and_belongs_to_many models, you don't have to use the fields_for and index trick. You can just use an association proxy method. Let's check it out:

class Reader < ActiveRecord::Base
has_and_belongs_to_many :blogs
end

<% form_for @reader do |f| %>
<%= f.text_field :name %
>

<%= f.collection_select :blog_ids, Blog.find(:all), :id, :name, {}, :multiple => true %>

<%= f.submit 'Create' %
>
<% end %>


generates:

<select id="reader_blog_ids" multiple="multiple" name="reader[blog_ids][]">
<option value="1">Martin Fowler</option>
<option value="2">Stephen Chu<
/option>
</select>


Processing ReadersController#create (for 127.0.0.1 at 2008-01-14 21:12:56) [POST]
Parameters: { "commit" => "Create",
"reader" => { "name" => "stephen chu"
"blog_ids" => ["1", "2"] },
"authenticity_token" => "238ba79b8282882ba01d840352616c2cc79280f0",
"action" => "create",
"controller" => "readers" }



And now, in my controller action, the creation of the reader only takes one line of code:

def create
@reader = Reader.new(params[:reader])


... thanks to the following has_many (or habtm) association assignment method:

@reader.blog_ids = [...many ids...]


So, stop doing any more params-munging code in your controller action like these:

def create
...
params[:blog_ids].each do |id|
@reader.blogs << Blog.find(id)
end
end


Get on the Rails bandwagon!

1 comments:

Kamal Fariz said...

Just a note that this does not work if your association is a has_many :through type. At least, that is what the docs say.