もた日記

くだらないことを真面目にやる

Railsメモ(14) : 多対多のリレーションをhas_many throughで作成する

これまでにSongモデルを作成しているが、今度はArtistモデルを作成する。
Songは"featuring"などで複数のArtistが関わっており、逆にArtistは複数のSongを持っているため、これらのモデルに多対多のリレーションを作成する。

Artistモデルの作成


まず、Artistモデル(ひとまずnameだけ)を下記コマンドで作成してdb:migrateを実行する。

$ rails g model Artist name:string
$ rake db:migrate


show-modelsで確認すると以下のようになる。

$ rails c
[1] pry(main)> show-models
Artist
  id: integer
  name: string
  created_at: datetime
  updated_at: datetime
Song
  id: integer
  title: string
  display_artist: string
  ranking: integer
  year: integer
  created_at: datetime
  updated_at: datetime

多対多のリレーションを作成


SongとArtistの間に多対多のリレーションを作成するために、まず中間モデルを作成する。

$ rails g model SongArtist song:references artist:references
$ rake db:migrate

続いて、それぞれのモデルのファイルを編集する。

app/models/song.rb

class Song < ActiveRecord::Base
 …省略…
  has_many :song_artists
  has_many :artists, through: :song_artists
end

app/models/artist.rb

class Artist < ActiveRecord::Base
  has_many :song_artists
  has_many :songs, through: :song_artists
end

show-modelsで以下のように多対多のリレーションが確認できる。

$ rails c
[1] pry(main)> show-models
Artist
  id: integer
  name: string
  created_at: datetime
  updated_at: datetime
  has_many :song_artists
  has_many :songs (through :song_artists)
Song
  id: integer
  title: string
  display_artist: string
  ranking: integer
  year: integer
  created_at: datetime
  updated_at: datetime
  has_many :artists (through :song_artists)
  has_many :song_artists
SongArtist
  id: integer
  song_id: integer
  artist_id: integer
  created_at: datetime
  updated_at: datetime
  belongs_to :artist
  belongs_to :song

rails consoleで多対多のリレーションを確認する


多対多のリレーションが正しく動作するかrails consoleで確認してみる。
試しに"Sam Smith"に関連するArtistデータを追加するが、関連する曲は3曲存在する。

Song.where("display_artist LIKE ?", "%Sam Smith%").select("id, title, display_artist")
  Song Load (2.0ms)  SELECT id, title, display_artist FROM "songs" WHERE (display_artist LIKE '%Sam Smith%')
+------+--------------+---------------------------------+
| id   | title        | display_artist                  |
+------+--------------+---------------------------------+
| 2410 | Stay with Me | Sam Smith                       |
| 2428 | Latch        | Disclosure featuring Sam Smith  |
| 2482 | La La La     | Naughty Boy featuring Sam Smith |
+------+--------------+---------------------------------+
3 rows in set

Artistを作成して中間テーブルにデータを追加するが、Artistを主とした追加や、Songを主としたデータの追加が可能である。

[1] pry(main)> sam = Artist.create name: "Sam Smith"
[2] pry(main)> dis = Artist.create name: "Disclosure"
[3] pry(main)> nau = Artist.create name: "Naughty Boy"
[4] pry(main)> Artist.all.select("id, name")
+----+-------------+
| id | name        |
+----+-------------+
| 1  | Sam Smith   |
| 2  | Disclosure  |
| 3  | Naughty Boy |
+----+-------------+
3 rows in set
[5] pry(main)> sam.songs << Song.find(2410)
[6] pry(main)> sam.songs << Song.find(2428)
[7] pry(main)> sam.songs << Song.find(2482)
[8] pry(main)> song1 = Song.find(2428)
[9] pry(main)> song1.artists << dis
[10] pry(main)> song2 = Song.find(2482)
[11] pry(main)> song2.artists << nau
[12] pry(main)> SongArtist.all.select("id, song_id, artist_id")
+----+---------+-----------+
| id | song_id | artist_id |
+----+---------+-----------+
| 1  | 2410    | 1         |
| 2  | 2428    | 1         |
| 3  | 2482    | 1         |
| 4  | 2428    | 2         |
| 5  | 2482    | 3         |
+----+---------+-----------+
5 rows in set

多対多のリレーションのデータは以下のような方法でアクセス可能になっている。

[1] pry(main)> Artist.find(1).songs.select("songs.id, songs.title, songs.display_artist")
+------+--------------+---------------------------------+
| id   | title        | display_artist                  |
+------+--------------+---------------------------------+
| 2410 | Stay with Me | Sam Smith                       |
| 2428 | Latch        | Disclosure featuring Sam Smith  |
| 2482 | La La La     | Naughty Boy featuring Sam Smith |
+------+--------------+---------------------------------+
3 rows in set
[2] pry(main)> Song.find(2428).artists.select("artists.id, artists.name")
+----+------------+
| id | name       |
+----+------------+
| 1  | Sam Smith  |
| 2  | Disclosure |
+----+------------+
2 rows in set

パーフェクト Ruby on Rails

パーフェクト Ruby on Rails