Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Rails Models Associations for has_many and belongs_to

Ok.. I'm new to Rails, and I know this has been asked before, but I'm still confused as to how to approach the following common problem. I can get the association to work, but having something magically work and starting rails with bad habits is not something I want to do.

Say I'm building a blog. I have two resources: Articles and Users. Each user has many articles, and each article belongs to one user:

rails g scaffold User name:string email:string
rails g scaffold Article user_id:integer title:string content:string

User Model:

class User < ActiveRecord::Base
  has_many :articles
end

Article Model:

class Article < ActiveRecord::Base
  belongs_to :user
end 

Now, on my articles index, I can do something like:

…table headers...
<% @articles.each do |article| %>
  <tr>
    <td><%= article.user.name %></td>
    <td><%= article.title %></td>
    <td><%= article.desc %></td>
    <td><%= article.content %></td>
    <td><%= link_to 'Show', article %></td>
    <td><%= link_to 'Edit', edit_article_path(article) %></td>
    <td><%= link_to 'Destroy', article, confirm: 'Are you sure?', method: :delete %></td>
  </tr>
<% end %>
</table>

And all I need for that model association for the User Name is to put "@articles = Article.all" on the index action before the respond_to. Pretty cool!

What if I want to list all these articles (I'm skipping paging here for the sake of simplicity) on my Home page, using the index action on my Home controller?

I know I can do something like this in the Home Controller:

class HomeController < ApplicationController
  def index
    @articles = Article.joins(:user)
  end 
end

…and then I can access this data on my home->index view:

<div class="row">
  <% @articles.each do |article| %>
    <div>
      <h3><%= link_to article.title,
        :controller => "articles", :action => "show", :id => article.id %></h3>
      <small>Posted on <%= article.created_at %> by
        <a href="#"><%= article.user.name %></a></small>
    </div>
  <% end %>
</div>

First Question: When accessing the User data for all the Articles, should I be using a :joins or an :includes? It seems they both work, but I wonder which one is right in this situation, and which one generally performs faster.

@articles = Article.joins(:user) 

-vs-

@articles = Article.includes(:user)

Second Question: In my scaffold for Article (building the migration), should I use user_id:integer or user:references. Do they do the same thing, or is one preferred over the other? If I use :integer as the field type, is it recommended that I also add an index for it (add_index :articles, :user_id)? I found a great RailsCast, and it does a great job of explaining, but I wonder if anyone else has another opinion.

If it helps, I'm on Rails 3.2.2.

Thanks!

like image 571
User 1058612 Avatar asked Jan 27 '12 18:01

User 1058612


2 Answers

  1. You should be using @articles = Article.all :include => :user to retrieve your records. Please read Rails :include vs. :joins for more information on why this is generally faster than :joins. (Basically, it gives you the same information without duplicates.)

  2. Rails has a shortcut for belongs_to associations when it comes to migrations. Use belongs_to :user and Rails will automatically include the user_id column (of type integer) for you.

Example:

class CreateArticle < ActiveRecord::Migration
  def self.up
    create_table :articles do |t|
      t.belongs_to :user # Creates 'user_id' column
      t.timestamps
    end
  end
end
like image 185
Graham Swan Avatar answered Sep 20 '22 10:09

Graham Swan


First question:

You want to retrieve all the articles with their users data in an efficient way, you must use

@articles = Article.includes(:user)

You'll get the list of all articles in your DB, each article with its user already fetched.

With @articles = Article.joins(:user) you'll get only the articles which have a User, and when you'll do article.user on any of these articles, it would generate a new SQL request.

For more information: http://guides.rubyonrails.org/active_record_querying.html (if you have not already read this set of guides, I strongly recommend you to do it now).

Second question:

I use the user_id:integer form. I'm not sure user:references can be used in the rails g scaffold command line. An index on the column 'articles.user_id' will improve the speed of retrieval when looking for articles of a specific user. Add this index only if your application will do this kind of search.

like image 22
Baldrick Avatar answered Sep 18 '22 10:09

Baldrick