Railsガイド6.0読み直す ~5章 Active Record の関連付け~
書いていく。
1 関連付けを使う理由
モデルとモデルの間には関連付けを行なう必要がありますが、その理由を御存じでしょうか。それは、関連付けを行う事であなたのコードでの共通操作をよりシンプルで簡単にするからです。
だよねー。
2 関連付けの種類
has_one :[子供のモデル] => 親から子供
belongs_to :[親のモデル] => 子供から親
主となる方が has_one で従となる方が belongs_to になる。
belongs_to
has_one
has_many
has_many :through
has_one :through
has_and_belongs_to_many
・belongs_to関連付けで指定するモデル名は必ず「単数形」
2-1 belongs_to
宣言を行ったモデルのすべてのインスタンスは、他方のモデルのインスタンスに「従属(belongs to)」します。
2-2 has_one
belongs_to
とは若干異なります。has_one
関連付けの場合は、その宣言が行われているモデルのインスタンスが、他方のモデルのインスタンスを「まるごと含んでいる」または「所有している」ことを示します
2-3 has_many
has_many
関連付けが使われている場合、「反対側」のモデルでは多くの場合belongs_to
が使われます。has_many
関連付けが使われている場合、そのモデルのインスタンスは、反対側のモデルの「0個以上の」インスタンスを所有します。
2-4 has_many :through
has_many :through
関連付けは、他方のモデルと「多対多」のつながりを設定する場合によく使われます。この関連付けは、2つのモデルの間に「第3のモデル」(joinモデル)が介在する点が特徴です。
2-5 has_one :through
has_one :through
関連付けは、他方のモデルに対して「1対1」のつながりを設定します。この関連付けは、2つのモデルの間に「第3のモデル」(joinモデル)が介在する点が特徴です。それによって、相手モデルの1つのインスタンスとマッチします
2-6 has_and_belongs_to_many
has_and_belongs_to_many
関連付けは、他方のモデルと「多対多」のつながりを作成しますが、through:
を指定した場合と異なり、第3のモデル(joinモデル)が介在しません(訳注: 後述するように結合用のテーブルは必要です)。
↑あまり使ったことない
2-7 has_one or belongs_to
区別の決め手となるのは外部キー(foreign key)をどちらに置くかです(外部キーは、belongs_to
を追加した方のモデルのテーブルに追加されます)。もちろんこれだけでは決められません。データの実際の意味についてもう少し考えてみる必要があります。has_one
というリレーションは、主語となるものが目的語となるものを「所有している」ということを表しています。そして、所有されている側(目的語)の方が、所有している側(主語)を指し示しているということも表しています。たとえば、「供給者がアカウントを持っている」とみなす方が、「アカウントが供給者を持っている」と考えるよりも自然です。つまり、この場合の正しい関係は以下のようになります。
class Supplier < ApplicationRecord has_one :account end class Account < ApplicationRecord belongs_to :supplier end
2-8 has_many :throughとhas_and_belongs_to_manyのどちらを選ぶか
どちらを使うかについてですが、経験上、リレーションシップのモデルそれ自体を独立したエンティティとして扱いたい(両モデルの関係そのものについて処理を行いたい)のであれば、中間にjoinモデルを使うhas_many :through
リレーションシップを選ぶのが最もシンプルです。リレーションシップのモデルで何か特別なことをする必要がまったくないのであれば、joinモデルの不要なhas_and_belongs_to_many
リレーションシップを使うのがシンプルです(ただし、こちらの場合はjoinモデルが不要な代わりに、専用のjoinテーブルを別途データベースに作成しておく必要がありますので、お忘れなきよう)。
2-9 ポリモーフィック関連付け
ポリモーフィック関連付けは、関連付けのやや高度な応用です。ポリモーフィック関連付けを使うと、ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現できます。
2-10 自己結合
データモデルを設計していると、時に自分自身に関連付けられる必要のあるモデルに出会うことがあります。たとえば、1つのデータベースモデルに全従業員を格納しておきたいが、マネージャーと部下(subordinate)の関係も追えるようにしておきたい場合が考えられます。この状況は、自己結合関連付けを用いてモデル化できます。
class Employee < ApplicationRecord has_many :subordinates, class_name: "Employee", foreign_key: "manager_id" belongs_to :manager, class_name: "Employee", optional: true end
上のように宣言しておくと、@employee.subordinates
と@employee.manager
が使えるようになります。
自己結合むずい。。
・もし、従業員にマネージャーがいたら、マネージャーを取得 → @employee.manager
・更に、マネージャーに部下がいたら、その部下も取れる
→ @employee.subordinates
わかりやすい
3 ヒントと注意事項
・reload? で更新
1. belongs_to
関連付けを使う場合は、外部キーを作成する必要があります。2. has_and_belongs_to_many
関連付けを使う場合は、適切なjoinテーブルを作成する必要があります。
Active Recordでスコープや次のオプションを使った場合、双方向の関連付けは自動的に認識されません。
:through
:foreign_key
Active Recordは:inverse_of
オプションを提供していて、これを使うと双方向の関連付けを明示的に宣言できます。
class Author < ApplicationRecord has_many :books, inverse_of: 'writer' end class Book < ApplicationRecord belongs_to :writer, class_name: 'Author', foreign_key: 'author_id' end
has_many
の関連付けを宣言するときに:inverse_of
オプションも含めることで、Active Recordは双方向の関連付けを認識するようになります。
4 関連付けの詳細情報
4.1 belongs_to
関連付けの詳細
belongs_to
関連付けは、別のモデルとの間に1対1の関連付けを作成します。データベースの用語で説明すると、この関連付けが行われているクラスには外部キーがあるということです。外部キーが自分のクラスではなく相手のクラスにあるのであれば、belongs_to
ではなくhas_one
を使う必要があります。
4.1.2 belongs_to
のオプション
belongs_to
関連付けでは以下のオプションがサポートされています。
:autosave
:class_name
:counter_cache
:dependent
:foreign_key
:primary_key
:inverse_of
:polymorphic
:touch
:validate
:optional
めちゃくちゃある。。w
4.2 has_one 関連付けの詳細
has_one
関連付けは他のモデルと1対1対応します。データベースの観点では、この関連付けでは相手のクラスが外部キーを持ちます。相手ではなく自分のクラスが外部キーを持っているのであれば、belongs_to
を使うべきです。
has_one
関連付けでは以下のオプションがサポートされます。
:as
:autosave
:class_name
:dependent
:foreign_key
:inverse_of
:primary_key
:source
:source_type
:through
:validate
これもまためちゃくちゃある。読むの疲れるレベル
books books<<(object, ...) books.delete(object, ...) books.destroy(object, ...) books=(objects) book_ids book_ids=(ids) books.clear books.empty? books.size books.find(...) books.where(...) books.exists?(...) books.build(attributes = {}, ...) books.create(attributes = {}) books.create!(attributes = {}) books.reload
ほぼ知ってたな。
5 シングルテーブル継承(STI)
感想
また読みに帰ってくる。量が多い。オプションも多い