Lesson Weekend

In this lesson, we'll set up a one-to-many relationship between albums and songs. We'll save our many-to-many relationship between albums and artists for a later lesson.

We've already created our albums table. Let's set up our songs table now:

$ rails g migration create_songs

Here's how our new migration should look. Note how Active Record automatically knows that we want to create a songs table because we named our migration create_songs.

db/migrate/20190611231043_create_songs.rb
class CreateSongs < ActiveRecord::Migration[5.2]
  def change
    create_table :songs do |t|
      t.column(:name, :string)
      t.column(:lyrics, :string)
      t.column(:album_id, :integer)

      t.timestamps
    end
  end
end

This looks similar to our albums table. The difference is that we add an album_id so we can make a one-to-many association between albums and songs.

Make sure to save the file and run the migration.

Let's make one more migration before we continue. This one will officially make album_id a foreign key:

rails g migration add_foreign_key_for_songs

Here's the migration:

db/migrate/20190611231043_add_foreign_key_for_songs.rb
class AddForeignKeyForSongs < ActiveRecord::Migration[5.2]
  def change
    add_foreign_key :songs, :albums
  end
end

Adding a foreign key is optional. Active Record offers this option so we can guarantee "referential integrity".

Our databases are now set up for an association between songs and albums. The next step is to add this relationship to our classes.

Let's start by creating a model with a Song class. We'll include the necessary code for the relationship as well:

app/models/song.rb
class Song < ApplicationRecord
  belongs_to :album
end

When a class has a "belongs to" relationship with another class, we use Active Record's belongs_to, which takes another class as an argument. Note that the class is indicated with a symbol: :album.

Now let's update our Album class:

app/models/album.rb
class Album < ApplicationRecord
  has_many :songs
end

Active Record is just using plain English. An instance of an Album has_many(:songs). Note the pluralized syntax of :songs here. Just like that, we've set up a one-to-many relationship between albums andsongs.

Note that this new relationship will automatically create several new methods, including Album#songs and Song.album.

One more thing: what if we want to make sure that an instance of Song is destroyed when the Album instance it belongs to is destroyed? This is easy, too:

app/models/album.rb
class Album < ApplicationRecord
  has_many :songs, dependent: :destroy
end

We simply add dependent: :destroy to our code.

We should still test this relationship, though. Wouldn't it be nice if we had a really easy way to test this kind of thing? Fortunately, there's shoulda-matchers, a gem that makes these tests much easier. We'll cover testing with shoulda-matchers soon.

Take a look at the Rails Guide on Active Record associations and the Rails API documentation on associations to learn more about what we can do with these tools.

Lesson 6 of 34
Last updated July 14, 2022