【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 |
データの例
- カミーユ:Python
- クワトロ:Java
- アムロ :JavaScript
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 |
データの例
- カミーユ:Java, JavaScript, Python
- クワトロ:Java, Python
- アムロ :JavaScript
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とか設定していくといいんでしょうが端折り(別の機会に...)