Ruby on Rails has six commonly used association types. These association types cover a wide range of relational patterns between models and fulfill the majority of use cases. They provide the necessary tools to establish relationships, retrieve associated data, and perform data manipulations efficiently.
The One-to-One (has_one) Association
In a one-to-one association, each record in one model is associated with one record in another model, and vice versa. This is typically achieved by using a foreign key column in one of the models to reference the primary key of the other model. For example, if you have a User model and an Profile model, each user may have only one profile, and each profile belongs to only one user.
To establish a one-to-one association in Rails, you need to set up the corresponding database tables and define the associations in the models.
1. Create the migration files to generate the User and Profile tables.
# Create User table
rails generate migration CreateUsers
# Create Profile table
rails generate migration CreateProfiles
2. In the migration files, you define the columns for each table, including a foreign key in the child table to establish the association.
# create_users.rb
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
# create_profiles.rb
class CreateProfiles < ActiveRecord::Migration[6.1]
def change
create_table :profiles do |t|
t.string :bio
# Foreign key for the one-to-one association
t.references :user, foreign_key: true
t.timestamps
end
end
end
3. Run the migration.
rails db:migrate
4. Next, you need to define the associations in the User and Profile models.
# user.rb
class User < ApplicationRecord
has_one :profile
end
# profile.rb
class Profile < ApplicationRecord
belongs_to :user
end
With these associations in place, you can now access and manipulate the associated data. For example, you can create the first user and profile, and associate them together.
user = User.create(name: "Steven")
profile = Profile.create(bio: "Full-Stack Engineer")
user.profile = profile
# Retrieve the associated data
user = User.find(1)
profile = user.profile
The One-to-Many (has_many) Association
In a one-to-many association, one record in one model is associated with multiple records in another model. This is achieved by using a foreign key column in the "many" model to reference the primary key of the "one" model. For example, if you have a User model and a Post model, each user can have multiple posts, but each post belongs to only one user.
1. To begin, create the migration files to generate the User and Post tables.
# Create User table
rails generate migration CreateUsers
# Create Post table
rails generate migration CreatePosts
2. In the migration files, we define the columns for each table, including a foreign key in the child table to establish the association.
# create_users.rb
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
# create_posts.rb
class CreatePosts < ActiveRecord::Migration[6.1]
def change
create_table :posts do |t|
t.string :title
t.text :content
# Foreign key for the one-to-many association
t.references :user, foreign_key: true
t.timestamps
end
end
end
3. Run the migration
rails db:migrate
4. Next, define the associations in the User and Post models.
# user.rb
class User < ApplicationRecord
has_many :posts
end
# post.rb
class Post < ApplicationRecord
belongs_to :user
end
With these associations in place, you can now access and manipulate the associated data. For example, you can create a new user and multiple posts associated with that user:
user = User.create(name: "Steven")
post1 = user.posts.create(title: "Rails Associations P1", content: "Content...")
post2 = user.posts.create(title: "Rails Associations P2", content: "Content...")
# Retrieve the associated data
user = User.find(1)
posts = user.posts
The Many-to-Many Association
Rails offers two ways to declare a many-to-many relationship between models. The choice between them depends on whether you need a simple association without additional attributes (has_and_belongs_to_many) or a more complex association with extra attributes and flexibility (has_many through:).
Method 1 - has_many :through
In a has_many :through association, multiple records in one model are associated with multiple records in another model. This is achieved by using a join table with foreign keys referencing the primary keys of both models. For example, if you have a User model and a Group model, each user can belong to many groups, and each group can have many users.
1. Create the migration files to generate the User, Group, and Memberships tables.
# Create User table
rails generate migration CreateUsers
# Create Group table
rails generate migration CreateGroups
# Create Memberships table (join table)
rails generate migration CreateMemberships
2. In the migration files, we define the columns for each table, including the foreign keys needed for association.
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
# create_groups.rb
class CreateGroups < ActiveRecord::Migration[6.1]
def change
create_table :groups do |t|
t.string :name
t.timestamps
end
end
end
# create_memberships.rb
class CreateMemberships < ActiveRecord::Migration[6.1]
def change
create_table :memberships do |t|
t.references :user, foreign_key: true
t.references :group, foreign_key: true
t.timestamps
end
end
end
3. Run the migration
rails db:migrate
4. Define the associations in the User, Group, and Membership models.
# user.rb
class User < ApplicationRecord
has_many :memberships
has_many :groups, through: :memberships
end
# group.rb
class Group < ApplicationRecord
has_many :memberships
has_many :users, through: :memberships
end
# membership.rb
class Membership < ApplicationRecord
belongs_to :user
belongs_to :group
end
With these associations in place, you can now access and manipulate the associated data. For example, you can create new users, groups, and memberships to establish a many-to-many relationship.
user1 = User.create(name: "John")
user2 = User.create(name: "Jane")
group1 = Group.create(name: "Ruby Enthusiasts")
group2 = Group.create(name: "Web Developers")
Membership.create(user: user1, group: group1)
Membership.create(user: user1, group: group2)
Membership.create(user: user2, group: group2)
# Retrieve the associated data
user = User.find(1)
groups = user.groups
group = Group.find(2)
users = group.users
Method 2 - has_and_belongs_to_many
Let's consider a scenario where we have two models: User and Role. A user can have multiple roles, and a role can be assigned to multiple users. We want to establish a many-to-many relationship between these models using the has_and_belongs_to_many association.
1. Create the migration files to generate the User, Role, and Memberships tables.
# Create User table
rails generate migration CreateUsers name:string
# Create Role table
rails generate migration CreateRoles name:string
2. Run the migration
rails db:migrate
3. Create a migration to create the join table that will connect the User and Role models.
rails generate migration CreateJoinTableUsersRoles users roles
4. In this migration file, update the change method with the following code. This will create a join table called users_roles with columns representing user_id and role_id.
class CreateJoinTableUsersRoles < ActiveRecord::Migration[6.0]
def change
create_table :users_roles, id: false do |t|
t.belongs_to :user
t.belongs_to :role
end
end
end
5. Run the migration
rails db:migrate
6. Define the associations in the User and Role models.
# user.rb
class User < ApplicationRecord
has_and_belongs_to_many :roles
end
# role.rb
class Role < ApplicationRecord
has_and_belongs_to_many :users
end
With the association set up, you can now perform operations using the has_and_belongs_to_many association.
user = User.create(name: "Steven")
role1 = Role.create(name: "Admin")
role2 = Role.create(name: "Editor")
user.roles << role1
user.roles << role2
# Retrieve the associated data
user.roles
role1.users