Active Record Associations for Beginners

Published: 2014-10-24

Relationships between the data in your models are defined using associations in Rails. Here is a brief overview of the associations I use the most and how to implement them in your Rails applications.

The Setup

All the examples in the article will be built and tested using the following versions:
  • Rails: 4.1.5
  • Ruby 2.1.2
  • MiniTest 5.4.1

To help with the testing side of things, you can read my article on Rails Automated Testing Setup for Beginners to help you get setup with automated testing if you haven't already.

Basic Principles of associations

When specifying an association between 2 models it is best to put the association on both sides of the Model. This allows your models to become self documenting so that you know what type of association your model has with other models.

Rails associations refer to the plural or singular of a model, depending on the type of association. It is important to note what you should be using and where you should be using it, as associations and queries will fail if the wrong one is used.

Typically only one Model/Table has a field that references the other model in the association. This model is considered the child model with the other model the parent.

One to Many

This is probably the most common association you will see when starting out. And Rails makes it easy with just 2 lines of code.

A simple example is a blog post that has comments. The Post model is the parent and the Comment model is the child.

So your Post model would have many comments, and your comments would belong to the post. You set this up in Rails as follows:

class Post < ActiveRecord::Base
  # no foreign key - no special column needed
  has_many :comments
end

class Comment < ActiveRecord::Base
  # foreign key - requires a database column: post_id of type: integer
  belongs_to :post     
end

The comments table would have a post_id field to reference the Post model that it belongs to. The foreign key field in the child table is the singular of the Parent model followed by _id.

How to query your One to Many records

With a One to Many setup, typically you may want to show all the children belonging to a parent in the same page. To reduce multiple database query's it is recommended to include the child table in the query so Rails does a database join query and only makes a single query to the database to get all the records (parent and children). You don't need to know how a join query works right now, Rails will take care of that for you.

Here is the simple (wrong) way to query a Post model and then show its comments.

post = Post.find(params[:id])
post.comments.each do |comment|
  # each comment will generate a database query
end
Not the ideal way as lots of comments will generate a lot of database queries.

By using the includes method, Rails looks at your associations and uses a single database query to get all the data. Post and comments are now together.

# notice how the includes use the plural of the child.
post = Post.includes(:comments).find(params[:id])

post.comments.each do |comment|
  # each comment will NOT generate a database query
  # this will make things faster
end
When generating your Parent object, include uses the plural of the child model.

One Model, Multiple Associations

Now say our Post model from above belonged to a category. To reduce duplication of category names, you would create a Category model and add associations to your Post models.

Here Category is the parent and Post is the child.

class Category < ActiveRecord::Base
  # no foreign key - no special column needed
  # uses plural of child model
  has_many :posts
end

class Post < ActiveRecord::Base
  # foreign key - requires a database column: category_id of type: integer
  belongs_to :category

  # comment association remains unchanged
  has_many :comments
end

# Comment model is unchanged.
class Comment < ActiveRecord::Base
  # foreign key - requires a database column: post_id of type: integer
  belongs_to :post     
end

This association plays out exactly the same as before with the same rules regarding database queries.

A category page that lists all the posts in that category:

category = Category.includes(:posts).find(params[:id])

category.posts.each do |post|
  # each post will NOT generate a database query
  # puts post.title
end

What the above shows is that your models can have multiple associations without any issues in Rails.

One to One

So what about models with a one to one relationship? Simple, just use has_one instead of has_many on the Parent model

class Customer < ActiveRecord::Base
  # no foreign key - no special column needed
  # uses singular of child model
  has_one :account
end

class Account < ActiveRecord::Base
  # foreign key - requires a database column: customer_id of type: integer
  belongs_to :customer
end

It works just the same, just make sure to use the singular of the child as their is only one.

customer = Customer.includes(:account).find(params[:id])

customer.account

Many to many

Continuing with our blog post theme, what about posts that have tags? In this case it would be a many to many relationship as there are many posts that have many tags. You might also want to find the posts that have a certain tag.

To use a many to many association you will use the has many through association which requires a third model/table to track the association between the two models. This table only needs 2 columns that are foreign keys for each model. For our example lets call this through model Tagging

Here is how we generate the tagging and tag models:

rails generate model Tagging post_id:integer tag_id:integer
rails generate model Tag name:string

Notice how only the through model needs foreign keys.

# the through model
class Tagging < ActiveRecord::Base
  belongs_to :post  # foreign key - programmer_id
  belongs_to :tag   # foreign key - project_id
end

class Tag < ActiveRecord::Base
  has_many :taggings
  has_many :posts, through: :taggings # notice the use of the plural model name
end

class Post < ActiveRecord::Base
  has_many :taggings
  has_many :tags, through: :taggings # notice the use of the plural model name
end

Access the tags

Now we can access the tags the same way we did with a many to one.

Show a post with all its tags

post = Post.includes(:tags).find(params[:id)
post.tags.each do |tag|
  # puts tag.name
end

Show a tag with all associated posts

tag = Tag.includes(:posts).find(params[:id])
tag.posts.each do |post|
  # puts post.title
end

Tree Relationship

Last we have a tree relationship. A good example of his is the classic folder hierarchy. Folders can have many sub folders and one parent folder. In this case the folder model needs to have associations with itself.

Here is the model being generated with the parent_folder_id column

rails generate model Folder name:string parent_folder_id:integer

Here are the associations

class Folder < ActiveRecord::Base
  belongs_to :parent_folder, class_name: Folder
  has_many :child_folders, class_name: Folder, foreign_key: "parent_folder_id"
end

The belongs_to and has_many work exactly the same as before, though this time with the class_name field and foreign_key field, you are building self joins within the same model so you can have a tree relationship.

The query works the same as before:

parent_folder = Folder.includes(:child_folders).first

parent_folder.child_folders.each do |child_folder|
  # puts child_folder
end

What next?

As you have seen, Rails makes it very easy to create associations between your models.
We have just scratched the surface of what you can do with associations, be sure to check the RailsGuides for Active Record Associations as it dives deeper into associations in Rails.

In the future when creating your Rails applications, start thinking about the relationships/associations between your data models before you create them to make sure that you are leveraging Rails associations to help make your job easier.

Resources

RailsGuides for Active Record Associations - A great resource that goes in depth on Active Record Callbacks.

Active Record Associations API Doc - More details and examples on callbacks.

Parameterize - API reference for the parameterize method in rails.

Try Method - API doc for the Try method

Rails Automated Testing Setup for Beginners - My article on how to setup MiniTest for easy automated testing.


comments powered by Disqus