Mastering ActiveRecord Associations in Ruby: A Comprehensive Guide
Tags: ruby, activerecord, beginners, programming
Published: 2023-05-23 21:10:36

## What are database associations?
Database associations are necessary for Ruby applications to manage the relationships between database tables. The associations establish relationships between related tables allowing you to properly link the crossover data within tables. Database associations also allow you to easily retrieve data that is related. With well-defined associations, information from one table can be easily obtained through another table. Along with this, the established relationships make handling CRUD operations and different tasks much more efficient. Lastly, complex relationships also become much easier to handle through database associations, by utilizing many of the different relationships managing your data becomes much more efficient. So with all that said, let's get into using ActiveRecord associations to show how easy managing database relationships can be.

ActiveRecord is a Ruby gem that was created to simplify the binding process between tables within a database. It allows us to use keywords to create relationships rather than building out our own object-relational mapper and manually writing complex SQL. You see, ActiveRecord generated your SQL code behind the scenes allowing you to focus on your application rather than writing out code for your database associations.

##Understanding ActiveRecord Associations 
Let's create a scenario. We want to create a web application to help us work out. To do this, we need to create a data table with all of the major muscle groups and a separate data table of exercises; each table has many different attributes.

Let's do this by creating each class using ActiveRecord:
```ruby
class MuscleGroup < ActiveRecord::Base
    
end
```

```ruby
class Exercise < ActiveRecord::Base
    
end
```

Once these are created, you are going to create a migration for each table by running: 
`
bundle exec rake db:create_migration NAME=create_muscle_groups
`
and
`
bundle exec rake db:create_migration NAME=create_exercises
`

This migration will create a .rb file with a timestamp at the beginning where you can create your table with all of its attributes. The timestamp is an important aspect of ActiveRecord because it ensures that the migrations are running in the intended order. 

In your migration file you can create your tables like so: 
```ruby
class CreateMuscleGroups < ActiveRecord::Migration[6.1]
  def change
    create_table :muscle_groups do |t|
      t.string :name
      t.string :image_url
    end
  end
end
```

```ruby
class CreateExercises < ActiveRecord::Migration[6.1]
  def change
    create_table :exercises do |t|
      t.string :name
      t.string :image_url
      t.string :how_to_do
      t.integer :muscle_group_id
    end
  end
end
```

In order to migrate the table, run `bundle exec rake db:migrate` after you add a new table or make changes to your table. 

You will notice the attribute `muscle_group_id`. This is our <b>foreign key</b>. Let's define foreign key and primary key.
##### Primary key
A primary key is a unique identifier for each row in a database table. This provides the uniqueness of the key so that each case can be distinguished. Examples of this would be things like `id`.
##### Foreign key
A foreign key is an identifier that refers to the primary key in another table. This establishes the relationship between the two tables. An example of this is the `muscle_group_id`.

Because of this foreign key, we now have a row in the exercise table that indicates, through the muscle group id, which muscle group the exercise `belongs_to`. 

In order to best make use of these tables, we need to form a relationship. Using ActiveRecord we can very easily do so through one of the types of associations this is the `has_many` and `belongs_to` association. Each major muscle group `has_many` exercises and each exercise `belongs_to` a muscle group.
To form this relationship, we would need to use these keywords in the class components for those tables. For example: 

```ruby
class MuscleGroup < ActiveRecord::Base
    has_many :exercises
end
```

```ruby
class Exercise < ActiveRecord::Base
    belongs_to :muscle_group
end
```
This indicates to ruby the relationship between muscle groups and exercises. As you can see, the `has_many` keyword is followed by the pluralized class. That is because there are many exercises whereas, the `belongs_to` keyword is followed by a singular class because there is only one muscle group for many of the exercises. 

## Seeding data and testing my code
All of these relationships are great; however, what if we want to test them? We need some data. You can create mock data in db/seeds.rb. Let's go back to our one-to-many relationship and create a muscle group and a couple of exercises within the seeds.rb file. 
```ruby
MuscleGroup.create(name: "Legs", image_url: "https://tse3.mm.bing.net/th?id=OIP.CCaTj7B5HI0e7pIotjn4PAHaE7&pid=Api&P=0&h=180")
```
```ruby
Exercise.create(name: "Back Squat", image_url: "https://julielohre.com/wp-content/uploads/2017/11/Barbell-Back-Squat.jpg", 
how_to_do: "Hold the barbell on your shoulders.  Keeping your back straight, squat down until the angel of your legs reaches 90 degrees or greater. Press up until you reach starting position.", muscle_group_id: MuscleGroup.find_by(name: "Legs").id)
Exercise.create(name: "Calf Raise", image_url: "https://i0.wp.com/bootcampmilitaryfitnessinstitute.com/wp-content/uploads/2018/02/Exercise-Calf-Raises-1.jpg?ssl=1", 
how_to_do: "Start with your feet flat on the ground.  Raise up on your toes as far as you can, squeezing the muscles in your calves.  Return to starting position.", 
muscle_group_id: MuscleGroup.find_by(name: "Legs").id)
```

Now to test my data, I want to run `bundle exec rake db:seed`. This will seed all of my mock data and I can play around with my code and make sure it works!

To get to the console let's run `bundle exec rake console`
We can now interact with the console and test our code using... ActiveRecord methods!

##ActiveRecord methods
There is a long list of ActiveRecord methods that allow us to interact with the database, here are a few and what they do: 

1. Creating Records:

.create: Creates a new record and saves it to the database.
.new: Creates a new record but does not save it to the database.
.save: Saves changes to an existing record or creates a new record and saves it.

2. Retrieving Records:

.find: Finds a record by its primary key (usually id).
.find_by: Finds the first record that matches the specified conditions.
.where: Retrieves records that match the specified conditions.
.first: Retrieves the first record from the database.
.last: Retrieves the last record from the database.
.all: Retrieves all records from the database.

3. Updating Records:

.update: Updates attributes of a record and saves it to the database.
.save: Saves changes to an existing record.

4. Deleting Records:

.destroy: Deletes a record from the database.

#### Examples:

```ruby
Exercise.create(name: "Pull-up", image_url: "https://tse4.mm.bing.net/th?id=OIP.3fI1p5ikgmNfCWjyMGPpAwHaHa&pid=Api&P=0&h=180", 
how_to_do: "Start hanging from the bar with your arms shoulder width apart.  Pull your body upward until your chin is over the bar.  Lower yourself back down to the starting position.", 
muscle_group_id: MuscleGroup.find_by(name: "Back").id) 
```

```ruby
Exercises.find(1)
=> #<MuscleGroup:0x0000000109837728
 =>#id: 3,
 =>#name: "Legs",
 =>#image_url: "https://tse3.mm.bing.net/th?=>#id=OIP.CCaTj7B5HI0e7pIotjn4PAHaE7&pid=Api&P=0&h=180"
```

These are just a few but give you a lot of power to interact with your database.

##But what if I wanted to get really specific about the muscle groups?
Many exercises actually hit different muscle groups. What then? Well, we can create a <strong>Many to Many relationship </strong>.
This is when the `has_many :through` keyword comes in. Let's make a new database table called SecondaryMuscle, this will be our JOIN table.
```ruby
class SecondaryMuscle < ActiveRecord::Base
    
end
```

and run 
`
bundle exec rake db:create_migration NAME=create_secondary_muscles
`
We now have created a migration and can create the table:
```ruby
class CreateSecondaryMuscles < ActiveRecord::Migration[6.1]
  def change
    create_table :secondary_groups do |t|
      t.string :name
      t.string :image_url
    end
  end
end
```
We now will add/ change our associations using the secondary muscles JOIN table. 

The SecondaryMuscles table JOINs the two together in a many-to-many association:
```ruby
class SecondaryMuscle < ActiveRecord::Base
  belongs_to :muscle_group
  belongs_to :exercise
end
```

The MuscleGroups associations now become: 
```ruby
class MuscleGroup < ActiveRecord::Base
  has_many :secondary_muscles
  has_many :exercises, through: :secondary_muscles
end
```
The Exercises associations have become:
```ruby
class Exercise < ActiveRecord::Base
  has_many :secondary_muscles
  has_many :muscle_groups, through: :secondary_muscles
end
```

Now, through a small amount of code, you have formed relationships that give you easily accessible information from each table. 


##ActiveRecord Macros
The `has_many`, `belongs_to`, and `has_many :through` are all ActiveRecord macros. There are a few other types that I won't be going into but could be helpful to know. These macros are:

`has_one` - this defines a one-to-one association between two models. This is done by setting up an association with a foreign key in one table that references the primary key in the other, very similar to one-to-many.

`has_and_belongs_to_many` - this defines a many-to-many association between two tables without having to create an intermediate model, for example, no need for our SecondaryMuscle model. 

`has_one :through`- this defines a one-to-one association through an intermediate model. Very similar to using the `has_many :through`.

To conclude, using ActiveRecord will make your life much easier as a ruby developer. It will allow you to spend more time creating your web application and less time trying to create relationships using handwritten SQL. If you want to dive even deeper, please take a look at [Link to ruby on rails: ActiveRecord](https://guides.rubyonrails.org/active_record_basics.html)