Many to many model relationships in Rails
One of the challenges I had early in learning Rails was setting up many-to-many relationships between models. Many tutorials and articles cover the simpler one-to-many and many-to-one relationships with ActiveRecord’s
belongs_to functions, respectively, but when trying to build anything more than a sample application you quickly realize that you need to set up slightly more complicated relationships.
I want to set up a many to many relationship between my user and team models where users can belong to many teams and teams can contain many users. Prior to setting up the relationship, my models are just basic ActiveRecord models generated from the rails model generator.
user.rb model (before)
class User < ActiveRecord::Base end
team.rb model (before)
class Team < ActiveRecord::Base end
To enforce the many to many relationship, I chose to implement the one of the ways suggested by the RailsCast Many-to-Many screencast, which involves
This approach involves creating another model specifically for handling the relationship mappings so that ActiveRecord can join the tables behind the scenes. Because I’m trying to join the users and teams table, I decided to name this model membership.
rails g model membership user_id:integer team_id:integer will create a model and migration for the new membership model.
Now, add lines 2 and 3 to set up the relationships to the user and team models.
membership.rb model (after)
class Membership < ActiveRecord::Base belongs_to :user belongs_to :team end
The membership model with the relationships above will represent another table that will be used for storing mappings between the user and team models. For example, the table may include data such as:
Sample membership table data
This sample data shows how the model can be used to look up many to many relationships.
- User 1 has many teams 1, 2, and 3
- Team 1 has many users 1, 2, 3, and 4
Meow, we can associate both the user and team models to have many memberships. In addition, we can use a
has_many override to tell active record that “a user has_many teams through the memberships model” and “a team has_many users through the memberships model”.
class User < ActiveRecord::Base has_many :memberships, :dependent => :destroy has_many :teams, through: :memberships end
class Team < ActiveRecord::Base has_many :memberships, :dependent => :destroy has_many :users, through: :memberships end
Now that the relationships are set up, assuming that you have ran
rake db:migrate to sync the new membership model, each model should be able to take advantage of the relationship.
@team = Team.first @team.users # array of users @user = User.first @user.teams # array of teams