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.

2
  • 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. Commented Feb 6, 2010 at 15:52
  • I've added the code for the associations, controller actions, and views. Thanks for your help. Commented Feb 6, 2010 at 19:33

7 Answers 7

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
Sign up to request clarification or add additional context in comments.

Comments

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.

1 Comment

That was my question (and my answer) and I can definitely tell you it worked :)
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

Comments

1

Try this:

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

5 Comments

Different error now: 'undefined method `with_indifferent_access' for "Coding":String' Parameters: {"commit"=>"Update", "authenticity_token"=>"fwCGGgcTKOSfxpFJMXmq7IUfRtfOvdOKl31Xys4TKC8=", "link"=>{"tags_attributes"=>{"name"=>"Coding"}, "uri"=>"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?
Can you please paste your whole form
I've added the form to the original post. Thanks for your help.
I have exactly the same problem, has_many with through and the error about "with_indifferent_access". Did you find a solution?
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.
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.

1 Comment

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

Comments

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| %>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.