29

I have two models, links and tags, associated through a third, link_tags. The following code is in my Link model.

Associations:

class Link < ActiveRecord::Base
  has_many :tags, :through => :link_tags
  has_many :link_tags

  accepts_nested_attributes_for :tags, :allow_destroy => :false, 
  :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
end

class Tag < ActiveRecord::Base
  has_many :links, :through => :link_tags
  has_many :link_tags
end

class LinkTag < ActiveRecord::Base
  belongs_to :link
  belongs_to :tag
end

links_controller Actions:

  def new
    @link = @current_user.links.build
    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @link }
    end
  end

  def create
    @link = @current_user.links.build(params[:link])

    respond_to do |format|
      if @link.save
        flash[:notice] = 'Link was successfully created.'
        format.html { redirect_to links_path }
        format.xml  { render :xml => @link, :status => :created, :location => @link }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @link.errors, :status => :unprocessable_entity }
      end
    end
  end

View code from new.html.erb:

<% form_for [current_user, @link], :url => account_links_path do |f| %>
<%= render :partial => "form", :locals => { :f => f } %>
<% end %>

And the corresponding partial:

  <%= f.error_messages %>

  <p>
    <%= f.label :uri %><br />
    <%= f.text_field :uri %>
  </p>
  <p>
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </p>

  <h2>Tags</h2>
  <% f.fields_for :tags_attributes do |tag_form| %>
  <p>
    <%= tag_form.label :name, 'Tag:' %>
    <%= tag_form.text_field :name %>
  </p>
  <% unless tag_form.object.nil? || tag_form.object.new_record? %>
  <p>
    <%= tag_form.label :_delete, 'Remove:' %>
    <%= tag_form.check_box :_delete %>
  </p>
  <% end %>
  <% end %>

  <p>
    <%= f.submit 'Update' %>
  </p>

The following line of code, in the create action in the Link controller throws an error:

@link = @current_user.links.build(params[:link])

The error: Tag(#-621698598) expected, got Array(#-609734898)

Are there additional steps needed in the has_many => :through case? These seem to be the only indicated changes for the basic has_many case.

Andrew C
  • 431
  • 1
  • 5
  • 8
  • I am unable to reproduce your problem based on the code you have posted. Could you post the association parts(has_many/belongs_to/etc lines) of your three models, both relevant controller actions(links#new, links#create) and any view code that has to do with the links form. – EmFi Feb 06 '10 at 15:52
  • I've added the code for the associations, controller actions, and views. Thanks for your help. – Andrew C Feb 06 '10 at 19:33

7 Answers7

9

Take a look at the line of your code

<% f.fields_for :tags_attributes do |tag_form| %>

You need to use just :tags instead of :tags_attributes. This will solve your issue

Make sure that You have build links and tags in your controller like

def new
  @link = @current_user.links.build
  @link.tags.build
end
Derek
  • 982
  • 1
  • 8
  • 19
Jyothu
  • 3,104
  • 17
  • 26
5

I found this here on stackoverflow:

Rails nested form with has_many :through, how to edit attributes of join model?

please tell me if it worked.

Community
  • 1
  • 1
Tiago
  • 2,966
  • 4
  • 33
  • 41
2

In order for this to work, you need to pass in the right params hash:

params = {
  :link => {
    :tags_attributes => [
      {:tag_one_attr => ...}, {:tag_two_attr => ...}
    ],
    :link_attr => ...
  }
}

And your controller will look like:

def create
  @link = Link.create(params[:link]) # this will automatically build the rest for your
end
jaredonline
  • 2,912
  • 2
  • 17
  • 24
1

Try this:

<% f.fields_for :tags_attributes do |tag_form| %>
  • 1
    Different error now: 'undefined method `with_indifferent_access' for "Coding":String' Parameters: {"commit"=>"Update", "authenticity_token"=>"fwCGGgcTKOSfxpFJMXmq7IUfRtfOvdOKl31Xys4TKC8=", "link"=>{"tags_attributes"=>{"name"=>"Coding"}, "uri"=>"http://stackoverflow.com", "title"=>"Stack Overflow"}} Thanks for the suggestion. Is there a solution to this? Is it trying to treat the value of one of the attributes as a hash? – Andrew C Feb 06 '10 at 09:42
  • Can you please paste your whole form – obiwanchinobi Feb 06 '10 at 09:51
  • I've added the form to the original post. Thanks for your help. – Andrew C Feb 06 '10 at 19:33
  • I have exactly the same problem, has_many with through and the error about "with_indifferent_access". Did you find a solution? – tbk Dec 02 '10 at 12:37
  • The solution is to not use Rails nested form helpers, you need to pass in a custom params hash, which means building your forms yourself. – jaredonline Mar 29 '11 at 20:57
0

In your controller in the new action (that loads the form partial), are you building a @tag through your link?

So you should see something along the lines of:

@link = Link.new
@tag = @link.tags.build

It might be best to post the contents of the new and create action of your links_controller.

Steve
  • 592
  • 9
  • 24
  • The controller actions are now in the original post. I'm not creating the tag in the new action. I was following along with the multi-model section here: http://guides.rubyonrails.org/getting_started.html , which didn't indicate controller changes would be necessary. That said, the new form does automatically generate a tag box, despite the absence of any tags. – Andrew C Feb 06 '10 at 19:37
0

try

<% f.fields_for :tags do |tag_form| %> 

(ie lose the _attributes in :tag_attributes) That's how I've usually done nested forms

soheildb
  • 1,490
  • 9
  • 9
0

You need to build a tag in your controller or in the view

def new
  @link = @current_user.links.build
  @link.tags.build
end

#in your view you can just use the association name
<% f.fields_for :tags do |tag_form| %>
Aaron Renoir
  • 4,283
  • 1
  • 39
  • 61