1

I'm reading Beginning Rails 3. It creates a blog with Users who can post Articles and also post Comments to these Articles. They look like this:

    class User < ActiveRecord::Base
      attr_accessible :email, :password, :password_confirmation
      attr_accessor :password

      has_many :articles, :order => 'published_at DESC, title ASC',
                          :dependent => :nullify
      has_many :replies, :through => :articles, :source => :comments

    class Article < ActiveRecord::Base
      attr_accessible :body, :excerpt, :location, :published_at, :title, :category_ids

      belongs_to :user
      has_many :comments

    class Comment < ActiveRecord::Base
      attr_accessible :article_id, :body, :email, :name
      belongs_to :article

in app/views/comments/new.html.erb there's a form which begins like this:

    <%= form_for([@article, @article.comments.new]) do |f| %>

My confusion lies in why form_for() has two parameters. What do they resolve to and why are they necessary?

thanks, mike

1
  • So... did we help, or you still need anything? :) Commented May 18, 2012 at 7:16

2 Answers 2

16

Actually, in your example, you are calling form_forwith one parameter (which is Array). If you check the documentation you will see parameters it expects: form_for(record, options = {}, &proc). In this case a record can be ActiveRecord object, or an Array (it can be also String, Symbol, or object that quacks like ActiveRecord). And when do you need to pass it an Array?

The simplest answer is, when you have a nested resource. Like in your example, you have defined Article has many Comments association. When you call rake routes, and have correctly defined routes, you will see that Rails has defined for you different routes for your nested resource, like: article_comments POST /article/:id/comments.

This is important, because you have to create valid URI for your form tag (well not you, Rails does it for you). For example:

form_for([@article, @comments])

What you are saying to Rails is: "Hey Rails, I am giving you Array of objects as a first parameter, because you need to know the URI for this nested resource. I want to create new comment in this form, so I will give you just initial instance of @comment = Comment.new. And please create this comment for this very article: @article = Article.find(:id)."

This is roughly similar to writing:

form_for(@comments, {:url => article_comments_path(@aticle.id)})

Of course, there is more to the story, but it should be enough, to grasp the idea.

Sign up to request clarification or add additional context in comments.

Comments

1

This is a form for commenting on an article. So you, you need the Article you're commenting on (@article) and a new Comment instance (@article.comments.new). The form action for this form will be something like:

/articles/1/comments

It contains the id of the article you're commenting on, which you can use in your controller.

If you omit the @article like this: form_for @article.comments.new, the form action will looks like this:

/comments

In the controller you would have no way of knowing to which article the comment belongs.

Note that for this to work, you need to define a nested resource in your routes file.

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.