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!

3 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.

Charles-Antoine said...

Hi, first, thanks for all those great tips. However, I am trying to figure out how to solve a "similar" problem.

I want to create many entries in a join table by selecting multiple elements in a select helper.

I am stuck with at least 2 problems:

1)Only one type of value can be in the select helper, either one of the non-join tables ids

2)I am not even able to recuperate the ids which are clearly sent to the server formatted like this (Parameters: { "other stuff" ,"record"=>{"period_id"=>["996332878", "996332879"]}}

Any suggestions would be greatly appreciated.

thanks!

Anonymous said...

Thanks a lot for this blog post! I totally missed the "*_ids" assignment method and it's making things so easy now :)