インドカレーファンクラブ

パソコン、カメラ

【Rails】中間テーブルに対してhas_one, has_manyする時のModel

中間テーブルの意義をより理解するため、僕が考えたことを追って書く

User1人に対して1つLanguageを設定する。みたいなことを考える

短絡的に考えるとこういうテーブルになると思うんだけど

User

  • name
  • language_id

Language

  • name

こんな感じにデータをいれたい

User

id name language_id
1 カミーユ 3
2 クワトロ 1
3 アムロ 2

Language

id name
1 Java
2 JavaScript
3 Python

この状態だとModelはこうなる

class User < ApplicationRecord
  # has_oneではない!
  belongs_to :language
end

class Language < ApplicationRecord
end

気持ち的にはUserが has_one :language したいんだけど実際は belongs_to
どうもおかしく感じる

どうすればいいのかTwitterで相談したら中間テーブルを設けるのが良さそうとのこと
確かに!(ここでタイトル回収)

テーブルはこうなる

User

id name
1 カミーユ
2 クワトロ
3 アムロ

UserLanguage

id user_id language_id
1 1 3
2 2 1
3 3 2

Language

id name
1 Java
2 JavaScript
3 Python

データの例

Model

class User < ApplicationRecord
    has_one :user_language
    has_one :language, through: :user_language
end

class UserLanguage < ApplicationRecord
    belongs_to :user
    belongs_to :language
end

class Language < ApplicationRecord
    has_many :user_language
end
rails g model user name:string
rails g model user_language user:belongs_to language:belongs_to
rails g model language name:string
u = User.new(name: 'カミーユ')
u.save!
l = Language.new(name: 'Java')
l.save!
ul = UserLanguage.new(user: u, language: l)
ul.save!

User.first.language.name # 'Java'

参考:https://railsguides.jp/association_basics.html#has-one-through%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91

ついでにUser1人に対して複数Languageを設定する。という話になったらこうなる

User

id name
1 カミーユ
2 クワトロ
3 アムロ

UserLanguage

id user_id language_id
1 1 1
2 1 2
3 1 3
4 2 1
5 2 3
6 3 2

Language

id name
1 Java
2 JavaScript
3 Python

データの例

Model

class User < ApplicationRecord
    has_many :user_languages
    has_many :languages, through: :user_language
end

class UserLanguage < ApplicationRecord
    belongs_to :user
    belongs_to :language
end

class Language < ApplicationRecord
    has_many :user_language
end
rails g model user name:string
rails g model user_language user:belongs_to language:belongs_to
rails g model language name:string
u = User.new(name: 'カミーユ')
u.save!
l = Language.new(name: 'Java')
l.save!
l2 = Language.new(name: 'JavaScript')
l2.save!
l3 = Language.new(name: 'Python')
l3.save!

ul = UserLanguage.new(user: u, language: l)
ul.save!
ul2 = UserLanguage.new(user: u, language: l2)
ul2.save!
ul3 = UserLanguage.new(user: u, language: l3)
ul3.save!

User.first.languages.map{|l| l.name} # [ Java, JavaScript, Python]

dependentとか設定していくといいんでしょうが端折り(別の機会に...)