## Adding a Second Model Now that you’ve seen how a model built with scaffolding looks like, it’s time to add a second model to the application. The second model will handle comments on blog posts. ### Generating a Model Models in Rails use a singular name, and their corresponding database tables use a plural name. For the model to hold comments, the convention is to use the name Comment. Even if you don’t want to use the entire apparatus set up by scaffolding, most Rails developers still use generators to make things like models and controllers. To create the new model, run this command in your terminal: $ rails generate model Comment commenter:string body:text post:references This command will generate four files: * `app/models/comment.rb` – The model * `db/migrate/20100207235629_create_comments.rb` – The migration * `test/unit/comment_test.rb and test/fixtures/comments.yml` – The test harness. First, take a look at `comment.rb`: @@@ ruby class Comment < ActiveRecord::Base   belongs_to :post end @@@ This is very similar to the `post.rb` model that you saw earlier. The difference is the line `belongs_to :post`, which sets up an Active Record *association*. You’ll learn a little about associations in the next section of this guide. In addition to the model, Rails has also made a migration to create the corresponding database table: @@@ ruby class CreateComments < ActiveRecord::Migration   def self.up     create_table :comments do |t|       t.string :commenter       t.text :body       t.references :post       t.timestamps     end   end   def self.down     drop_table :comments   end end @@@ The `t.references` line sets up a foreign key column for the association between the two models. Go ahead and run the migration: $ rake db:migrate Rails is smart enough to only execute the migrations that have not already been run against the current database, so in this case you will just see: ==  CreateComments: migrating ============ -- create_table(:comments)    -> 0.0017s ==  CreateComments: migrated (0.0018s) === ### Associating Models Active Record associations let you easily declare the relationship between two models. In the case of comments and posts, you could write out the relationships this way: * Each comment belongs to one post * One post can have many comments In fact, this is very close to the syntax that Rails uses to declare this association. You’ve already seen the line of code inside the Comment model that makes each comment belong to a Post: @@@ ruby class Comment < ActiveRecord::Base   belongs_to :post end @@@ You’ll need to edit the `post.rb` file to add the other side of the association: @@@ ruby class Post < ActiveRecord::Base   validates :name,  :presence => true   validates :title, :presence => true,                     :length => { :minimum => 5 }   has_many :comments end @@@ These two declarations enable a good bit of automatic behavior. For example, if you have an instance variable `@post` containing a post, you can retrieve all the comments belonging to that post as the array `@post.comments`.

For more information on Active Record associations, see the Active Record Associations guide.

### Adding a Route for Comments As with the `home` controller, we will need to add a route so that Rails knows where we would like to navigate to see `comments`. Open up the `config/routes.rb` file again, you will see an entry that was added automatically for posts near the top by the scaffold generator, `resources :posts`, edit it as follows: @@@ ruby resources :posts do   resources :comments end @@@ This creates `comments` as a *nested resource* within `posts`. This is another part of capturing the hierarchical relationship that exists between posts and comments.

For more information on routing, see the Rails Routing from the Outside In guide.

### Generating a Controller With the model in hand, you can turn your attention to creating a matching controller. Again, there’s a generator for this: $ rails generate controller Comments This creates four files and one empty directory: * `app/controllers/comments_controller.rb` – The controller * `app/helpers/comments_helper.rb` – A view helper file * `test/functional/comments_controller_test.rb` – The functional tests for the controller * `test/unit/helpers/comments_helper_test.rb` – The unit tests for the helper * `app/views/comments/` – Views of the controller are stored here Like with any blog, our readers will create their comments directly after reading the post, and once they have added their comment, will be sent back to the post show page to see their comment now listed. Due to this, our `CommentsController` is there to provide a method to create comments and delete SPAM comments when they arrive. So first, we’ll wire up the Post show template (`/app/views/posts/show.html.erb`) to let us make a new comment: @@@ html

<%= notice %>

  Name:   <%= @post.name %>

  Title:   <%= @post.title %>

  Content:   <%= @post.content %>

Add a comment:

<%= form_for([@post, @post.comments.build]) do |f| %>   
    <%= f.label :commenter %>
    <%= f.text_field :commenter %>   
  
    <%= f.label :body %>
    <%= f.text_area :body %>   
  
    <%= f.submit %>   
<% end %> <%= link_to 'Edit Post', edit_post_path(@post) %> | <%= link_to 'Back to Posts', posts_path %> | @@@ This adds a form on the Post show page that creates a new comment, which will call the `CommentsController` create action, so let’s wire that up: @@@ ruby class CommentsController < ApplicationController   def create     @post = Post.find(params[:post_id])     @comment = @post.comments.create(params[:comment])     redirect_to post_path(@post)   end end @@@ You’ll see a bit more complexity here than you did in the controller for posts. That’s a side-effect of the nesting that you’ve set up; each request for a comment has to keep track of the post to which the comment is attached, thus the initial find action to the Post model to get the post in question. In addition, the code takes advantage of some of the methods available for an association. We use the `create` method on `@post.comments` to create and save the comment. This will automatically link the comment so that it belongs to that particular post. Once we have made the new comment, we send the user back to the original post using the `post_path(@post)` helper. As we have already seen, this calls the `show` action of the `PostsController` which in turn renders the `show.html.erb` template. This is where we want the comment to show, so let’s add that to the `app/views/posts/show.html.erb`. @@@ html

<%= notice %>

  Name:   <%= @post.name %>

  Title:   <%= @post.title %>

  Content:   <%= @post.content %>

Comments

<% @post.comments.each do |comment| %>   

    Commenter:     <%= comment.commenter %>   

  

    Comment:     <%= comment.body %>   

<% end %>

Add a comment:

<%= form_for([@post, @post.comments.build]) do |f| %>   
    <%= f.label :commenter %>
    <%= f.text_field :commenter %>   
  
    <%= f.label :body %>
    <%= f.text_area :body %>   
  
    <%= f.submit %>   
<% end %>
<%= link_to 'Edit Post', edit_post_path(@post) %> | <%= link_to 'Back to Posts', posts_path %> | @@@ Now you can add posts and comments to your blog and have them show up in the right places.