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