【Rails】図解形式で理解する《アソシエーション解説書》

Rails

アソシエーションとは?

アソシエーションとは、テーブル同士の関連付け(リレーションシップ)をモデル上の関係として操作出来るようにする仕組みのことです。

リレーションシップ
リンクをコピーしました

リレーションシップとは、テーブル同士の関連付けのことです。

商品をインターネット上で販売しているサイトを例にしてリレーションシップを確認してみます。各商品にはレビューが存在します。

この関係性をテーブルで表現すると、productsテーブルにあるid列の主キーreviewsテーブルにあるproduct_idカラムの外部キー制約によって実現することが出来ます。

外部キー、主キーの説明

主キーとは、商品の情報を一意に識別するためのものです。そして、外部キーとはどの商品にレビューが対応しているのか識別する為のものです。外部キー制約をつけたカラムには、対応する主キーの値を入れることで表現することが出来ます。

外部キー制約の付いたカラム名は「参照するテーブル名(単数形)_id」にします。

アソシエーションのコードを比較
リンクをコピーしました

先ほどのテーブル関係からid=1の商品(冷蔵庫)に属しているレビューを取得するにはどの様にすれば良いでしょうか?

テーブルからデータを取得する
アソシエーションを使用しない場合と使用した場合でレビューを取得していきます。

アソシエーションを使用しない場合
リンクをコピーしました

アソシエーションを特に使用しない場合は、下記の様にproductsテーブルに対応するProductモデルでfindメソッドを利用して、id=1のレコードを取得します。

外部キーの値は主キーの値が入っているので、whereメソッドに外部キーを指定してid=1の商品に属しているレビューを取得します。

コンソール | アソシエーションを使用しない場合
1
2
@product = Product.find(1)
@reviews = Review.where(product_id: @product.id)

アソシエーションを使用しない場合だと、外部キーを気にしてコードを記述しなければいけません。

アソシエーションを使用する場合
リンクをコピーしました

アソシエーションを使用する場合は、「Productオブジェクト.reviews」でその商品に属しているレビューを全て取得する事が出来ます。

コンソール | アソシエーションを使用する場合
1
2
@product = Product.find(1)
@reviews = @product.reviews

この様にアソシエーションを使用する事で、主キーや外部キーなどのテーブル同士の関連付けを直感的に記述する事が出来るのです。

ER図でテーブル同士の関係性を整理
リンクをコピーしました

アソシエーションを使う際に、テーブル同士の関係性を理解している必要があります。テーブル同士の関係性を可視化して整理する為に、ER図(Entity-Relationship Diagram)というものを使います。

usersテーブル、reviewsテーブル, productsテーブルの3つの関係性を例にしてER図で確認してみましょう。

ER図の例

ER図は、IE表記法という書き方で記述されます。
下記の表にある様に3つの記号を使ってテーブル同士の関係性を記述します。

記号 意味
○ (白丸) 0
1
鳥足 (3本に広がる線)

3つのテーブルのER図では、ユーザーと商品の関係は「ユーザー1に対して商品が下限が0で上限が多」という意味になります。つまり、ユーザー1に対しての商品は0以上を表します。

この様にER図を使うと、複数あるテーブル同士の関係を視覚的に整理する事が出来ます。

belongs_toメソッド
リンクをコピーしました

belongs_toメソッドは、参照元テーブル(外部キーを持つテーブル)から参照先テーブル(主キーを持つテーブル)にアクセスする場合に定義します。

belongs_toメソッドが必要な場合とは、どの様な関係性の事なのでしょうか?商品とレビューの関係性を参考にして確認していきましょう。

belongs_toメソッドが必要な関係性とは?
リンクをコピーしました

先ほどの商品とレビューの関係性は、下記の図の様に複数のレビューが各商品に必ず属しています。レビューは商品がなければ存在しません。

商品とレビューの関係性

つまり「レビューは必ずどれか1つの商品に属している(Review belongs to a Product)」と言えます。この様な関係性の場合に、belongs_toメソッドを使います。「belong to」は英語で「〜に属する」という意味です。

belongs_toメソッドを定義する
リンクをコピーしました

belongs_toメソッドを定義するには、下記のコードの様にモデルクラスに定義します。

モデルクラス | belongs_toメソッドを定義する
1
2
3
class モデル名 < ActiveRecord::Base
  belongs_to :関連名
end

「モデル名はbelongs_toの引数に指定した関連名に属する」という意味になります。

モデル名はbelongs_toの引数に指定した関連名に属する

belongs_toメソッドを定義する事で、参照先(関連する主キーがあるテーブル)の情報が現在のモデルを経由して取得出来る様になります。belongs_toメソッドの引数の関連名は単数形になるので注意してください。

Reviewモデルにbelongs_toメソッドを定義する
リンクをコピーしました

先ほどの商品とレビューの「レビューは必ずどれか1つの商品に属している(Review belongs to a Product)」という関係性を定義していきます。

商品のテーブルはproductsテーブル、レビューのテーブルはreviewsテーブルです。reviewsテーブルの外部キーproduct_idで対応するproductsテーブルのレコードを参照します。

productsテーブルとreviewsテーブルの関係

複数のレビューは、1つの商品に属しているので下記の様にbelongs_toメソッドを定義する事が出来ます。

Reviewモデル | belongs_toメソッドを定義する
1
2
3
class Review < ActiveRecord::Base
  belongs_to :product
end

belongs_toメソッドをReviewモデルに定義し引数にproductsテーブルに対応するモデル名を記述します。

has_manyメソッド
リンクをコピーしました

先ほどのbelongs_toメソッドの時は、複数のレビューは1つの商品に属すという関係でしたが、商品側から見たらどうなるでしょうか?

商品側から見たreview

商品側からレビューをみると、1つの商品は複数のレビューを持っています(Product has many Reviews)。この様な関係を1:nと言います。商品1つに対してレビューは0以上あるのでnと表現しています。

1:nの関係は、has_manyメソッドを使って定義する事が出来ます。

has_manyメソッドを定義する
リンクをコピーしました

has_manyメソッドは、下記のコードの様に1:nの1のモデルクラス側に定義します。

モデルクラス | has_manyメソッドを定義する
1
2
3
class モデル名 < ActiveRecord::Base
  has_many :関連名
end

上記は、「1つのモデルは複数の関連名を持つ」という意味になります。has_manyメソッドの引数の関連名は複数形になるので注意してください。

Productモデルにhas_manyメソッドを定義する
リンクをコピーしました

先ほどの「1つの商品は複数のレビューを持つ(Product has many Reviews)」という1:nの関係性をhas_manyメソッドで定義します。

モデルクラス | 構文
1
2
3
class Product < ActiveRecord::Base
  has_many :reviews
end

上記は、1つのproductオブジェクトに対して、複数のReviewオブジェクトが存在しますという意味になります。複数のオブジェクトなので関連名(reviews)は複数形になっています。

Productモデル経由でReviewモデルの情報を取得する
リンクをコピーしました

モデルクラスにhas_manyメソッドを定義したことによって、Productモデル経由でReviewモデルの情報を取得出来る様になりました。

商品id=1のレビューを全て取得する例をみていきましょう。まずは、商品id=1のオブジェクトを取得します。

controller側 | アソシエーションを使ってデータを取得する
1
2
3
def hasmany
  @product = Product.find(1) # オブジェクトを取得
end

そして、情報を取得するには「オブジェクト.関連名」で関連するオブジェクトを取得する事が出来ます。

view側 | アソシエーションを使ってデータを取得する
1
2
3
4
5
6
<h2><%= @product.name %></h2>
<ul>
  <% @product.reviews.each do |review| %> <%# オブジェクト.関連名で取得 %>
    <li><%= review.body %></li>
  <% end %>
</ul>

上記はproductオブジェクトには、複数のレビューが存在するのでeachメソッドで1つ1つレビューのレコードを取り出しています。

中間テーブルとthroughオプション
リンクをコピーしました

pikawakaは、どの様なメンバーと部署で構成されているのか見てみましょう。pikawakaの宮嶋 勇弥さんは、開発部と記事部の責任者です。これは1人のメンバーが複数の部署に属していると言えます。

1人のメンバーが複数の部署に属す

次に開発部の部署を見てみると複数のメンバーがいます。1つの部署には複数のメンバーが属していると言えます。

メンバーと部署の関係

メンバーは複数の部署を持ち、部署も複数のメンバーを持つというお互いに1つに対して複数存在している関係を多対多の関係と呼びます。

多対多をテーブル間のリレーションで表現するには?
リンクをコピーしました

先ほどのメンバーと部署の多対多の関係をテーブルで確認します。メンバーのテーブルをusersテーブル、部署のテーブルはdepartmentsテーブルとします。

宮嶋さんは開発と記事の2つの部署に所属しています。一方で、開発部は宮嶋さんと高橋さんが所属しているので、1人のユーザーは複数の部署に所属できるし、1つの部署は複数の人が入れるので、ユーザーと部署は多対多の関係と言えます。

多対多のテーブル

この様な多対多の関係をテーブルにする場合に、中間テーブルというテーブルが必要になります。

belongs_toでは、1つの外部キーを使って参照元テーブルから参照先テーブルを見ていましたが、中間テーブルでは、usersテーブルとdepartmentsテーブルの2つのテーブルをお互いに参照する必要があります。

多対多の関係

それには、中間テーブル(authorizations)に、usersテーブルを参照するための外部キー制約のカラム(user_id)とdepartmentsテーブルを参照するための外部キー制約のカラム(department_id)を持たなければなりません。

多対多の関連を定義する
リンクをコピーしました

多対多の関連を定義するには、モデルクラスにhas_manyメソッドのthroughオプションを使います。throughは、「経由する」という意味なので中間テーブルを経由して関連先のオブジェクトを取得することが出来ます。

has_many throughオプションを定義
1
2
3
4
class モデル名 < ActiveRecord::Base
  has_many :関連名, through: :中間テーブル名
  has_many :中間テーブル名 #おまじない
end

そして3行目にある「has_many :中間テーブル」は、throughオプションを使う時に必要なおまじないだと覚えて置きましょう。

メンバーと部署の多対多を定義する
リンクをコピーしました

先ほどのメンバーと部署の多対多の関係をhas_manyメソッドのthroughオプションを使って定義していきます。

まずは、usersテーブルに対応するUserモデルのファイルに定義していきます。下記の「has_many :departments, through: :authorizations」は、「1つのUserオブジェクトはAuthorizationモデルを経由して複数のDepartmentオブジェクトを持っている」という意味になります。

has_many throughオプションを定義
1
2
3
4
class User < ActiveRecord::Base
    has_many :departments, through: :authorizations
    has_many :authorizations #おまじない
end

Departmentモデルも同様にhas_manyメソッドのthroughオプションを定義していきます。

has_many throughオプションを定義
1
2
3
4
class Department < ActiveRecord::Base
    has_many :users, through: :authorizations
    has_many :authorizations #おまじない
end

authorizationsテーブルの外部キーは、usersテーブルとdepartmentsテーブルを参照するために存在します。その為、中間テーブルであるauthorizationsテーブルに対応するAuthorizationモデルには、belongs_toメソッドを定義します。 (Authorization belongs to User, Authorization belongs to Department)

belongs_toを定義
1
2
3
4
class Authorization < ActiveRecord::Base
    belongs_to :user
    belongs_to :department
end

そしてauthorizationsテーブルですが、外部キーのカラム以外にもroleというカラムがあります。

authorizationsテーブル

Authorizationは権限という意味です。authorizationsテーブルのroleカラムには、「責任者」か「メンバー」の値が入ります。これは、メンバーの部署での役割が責任者なのか、部署のメンバーという意味になります。

authorizationsテーブルにroleカラムを持たせる理由
リンクをコピーしました

roleカラムは、usersテーブルで良さそうですが、usersテーブルにするとメンバーが複数の部署に所属していた場合に役割(role)は1つしか指定出来なくなります。

usersテーブルにroleカラムがある場合に、宮嶋 勇弥さんが開発と記事の部署に所属していて、開発部では責任者の役割があるが、記事部では責任者ではなくメンバーであった時にusersテーブルの宮嶋 勇弥さんの役割(role)には1つしか入れることが出来ないのです。

多対多で定義すると何が出来るのか?
リンクをコピーしました

宮嶋 勇弥さん(id=1)の部署のレコードを取得する例を見ていきましょう。

下記のコードは、まずfindメソッドでusersテーブルから宮嶋 勇弥さんのオブジェクトを取得し、「オブジェクト.departments」で宮嶋 勇弥さんの所属する部署のレコードを全て取得しています。

id=1の宮嶋 勇弥さんの部署のレコードを全て取得する
1
2
3
4
5
6
7
user = User.find(1) # id=1のメンバーを取得
=> #<User id: 1, name: "宮嶋 勇弥", created_at: "2019-11-22 08:18:59", updated_at: "2019-11-22 08:18:59">

user.departments # id=1のメンバーの部署のレコードを全て取得する
=> #<ActiveRecord::Associations::CollectionProxy
 [#<Department id: 1, name: "開発", created_at: "2019-11-22 08:20:56", updated_at: "2019-11-22 08:20:56">, 
#<Department id: 2, name: "記事" created_at: "2019-11-22 08:21:02", updated_at: "2019-11-22 08:21:02">]>

このコードの実行時の流れは、まず中間テーブルのuser_idを見にいき、user_idが1のレコードのdepartment_id(1と2)を参照してdepartmetsテーブルの1と2のレコードを取得します。

コードが実行される時、中間テーブルのauthorizationsテーブルはあくまでも経由するだけで実際に取得しているのはdepartmentsテーブルのレコードだという事に注意してください。

もちろん部署から部署に属するメンバーも取得することが出来ます。

id=1の部署のレコードを全て取得する
1
2
3
4
5
6
7
department = Department.find(1) # id=1の部署を取得
=> #<Department id: 1, name: "開発", created_at: "2019-11-22 08:20:56", updated_at: "2019-11-22 08:20:56">

department.users # id=1の部署に属するメンバーのレコードを全て取得する
=> #<ActiveRecord::Associations::CollectionProxy 
[#<User id: 1, name: "宮嶋 勇弥", created_at: "2019-11-22 08:18:59", updated_at: "2019-11-22 08:18:59">, 
#<User id: 3, name: "高橋大地", created_at: "2019-11-22 08:19:42", updated_at: "2019-11-22 08:19:42">]>

departmentsテーブルからusersテーブルの関連するレコード取得の例
usersテーブルからdepartmentsテーブルのレコードを取得していた時と同様に、コードが実行される時、中間テーブルのauthorizationsテーブルはあくまでも経由するだけです。実際に取得しているのはusersテーブルのレコードです。

SQLで確認してみよう
リンクをコピーしました

先ほどの部署に所属するメンバーを取得するコードをもう一度確認してみましょう。

id=1の部署のレコードを全て取得する
1
2
3
4
5
6
7
department = Department.find(1) # id=1の部署を取得
=> #<Department id: 1, name: "開発", created_at: "2019-11-22 08:20:56", updated_at: "2019-11-22 08:20:56">

department.users # id=1の部署に属するメンバーのレコードを全て取得する
=> #<ActiveRecord::Associations::CollectionProxy 
[#<User id: 1, name: "宮嶋 勇弥", created_at: "2019-11-22 08:18:59", updated_at: "2019-11-22 08:18:59">,
 #<User id: 3, name: "高橋大地", created_at: "2019-11-22 08:19:42", updated_at: "2019-11-22 08:19:42">]>

実は、4行目の「department.users」では下記の様なSQLが発行されているのです。

department.usersで発行されるSQL文
1
2
3
4
SELECT * FROM `users`
  INNER JOIN `authorizations` 
    ON `users`.`id` = `authorizations`.`user_id` 
      WHERE `authorizations`.`department_id` = 1

このSQLを理解するために、まずはINNER JOINでテーブルが結合されるまでを確認します。(理解しやすい様にSELECT文で全てのカラムを取得する様にしています。)

INNER JOINでテーブルが結合されるまで

上記の画像の様に、INNER JOINは、ON以降に記述された結合条件にしたがってusersテーブルをauthorizatinsテーブルを結合します。

このSQLにWHERE文を追加してみます。このWHERE文は、authorizationsテーブルのuser_idが1のレコードに絞り込みます。

WHERE文を追加

WHERE文によってレコードが絞り込まれたのが分かります。この状態ではusersテーブル以外のauthorizationsテーブルのカラムがまだ含まれていますね。

現在のSELECT文ではテーブル結合を分かりやすくする為に全てのカラム(*)を取得していたので、元のSELECT文に戻して必要なカラムのみを取得します。

SQLのSELECT文を追加

作成されたテーブルは、authorizationsテーブルを通して部署に対応するメンバーのレコードを取得している事が分かりました。今回のコードを再度確認してみましょう。

id=1の部署のレコードを全て取得する
1
2
3
4
5
6
7
department = Department.find(1) # id=1の部署を取得
=> #<Department id: 1, name: "開発", created_at: "2019-11-22 08:20:56", updated_at: "2019-11-22 08:20:56">

department.users # id=1の部署に属するメンバーのレコードを全て取得する
=> #<ActiveRecord::Associations::CollectionProxy
[#<User id: 1, name: "宮嶋 勇弥", created_at: "2019-11-22 08:18:59", updated_at: "2019-11-22 08:18:59">, 
#<User id: 3, name: "高橋大地", created_at: "2019-11-22 08:19:42", updated_at: "2019-11-22 08:19:42">]>

「department.users」で関連先のインスタンスを取得出来ていたのは、下記の様なSQLが裏側で発行されていたからでした。

department.usersによって発行されるSQL
1
2
3
4
SELECT * FROM `users`
  INNER JOIN `authorizations`
    ON `users`.`id` = `authorizations`.`user_id`
       WHERE `authorizations`.`department_id` = 1

この様なSQLによって、中間テーブルのアソシエーションを使って関連先のテーブルのインスタンスを取得することが出来るのです。

has_oneメソッド
リンクをコピーしました

has_oneメソッドは、1対1の関係を定義します。1対1の関係とは、どのような関係のことなのでしょうか?

例えば、メンバーの宮嶋さんはPikawakaのアカウントを1つ持っています。アカウントを複数持つことはありません。

ユーザーとアカウントの関係

これは、1人のメンバーが1つのアカウントを持っている関係です。この様な関係を1対1の関係と言い、has_oneメソッドで定義する事が出来ます。

1対1をテーブル間のリレーションで表現するには?
リンクをコピーしました

1対1の関係をテーブル間で表現するには、参照元テーブルに外部キー制約の付いたカラムを持たせる事で表現することが出来ます。

先ほどのメンバーとアカウントの1対1の関係をテーブルで確認していきましょう。メンバーのテーブルをusersテーブル、アカウントのテーブルをaccountsテーブルとします。

accountsテーブルとusersテーブル

この場合の参照先テーブルがusersテーブル、参照元テーブルがaccountsテーブルなので、accountsテーブルには、外部キー制約の付いたuser_idカラムを持たせる事で参照できる様になります。

has_oneメソッドを定義する
リンクをコピーしました

has_oneメソッドを定義するには、主従関係の主にあたるモデルの方に定義します。

モデルクラス| has_oneメソッドを定義する
1
2
3
class モデル名 < ActiveRecord::Base
  has_one :関連名 # 単数形
end

has_oneメソッドは、1対1の関係を定義します。これによって関連付くオブジェクトは1つになるので、関連名は単数形になります。

Userモデルにhas_oneメソッドを定義しよう
リンクをコピーしました

それでは、先ほどの例を参考にしてhas_oneメソッドを定義しましょう。
usersテーブルに対応するのはUserモデルになるので、下記の様にhas_oneメソッドを定義します。

user.rb | has_oneメソッドを定義する
1
2
3
class User < ActiveRecord::Base
    has_one :account # 単数形
end

1人のUserは、1つのAccountを持つ関係(User has one account)でした。ユーザーに関連するアカウントは1つなので、関連名は単数形のaccountになります。

そして、accountsテーブルに対応するモデルはAccountモデルになるので、下記の様にbelongs_toメソッドを定義します。

account.rb | belongs_toメソッドを定義する
1
2
3
class Account < ActiveRecord::Base
    belongs_to :user 
end

belongs_toメソッドの引数の関連名は、1つのアカウントは1人のユーザーに属しているので、単数形になります。

ユーザーの持つアカウントを取得してみよう
リンクをコピーしました

has_oneメソッドで関連付けしたので、usersテーブルのname='宮嶋勇弥'さんの持つアカウントを取得して、account_numberを表示させていきます。

accountsテーブルとusersテーブル

まずは、controllerでユーザー情報を取得します。

controller側 | name='宮嶋勇弥'のユーザーオブジェクトを取得する
1
2
3
def hasone
  @user = User.find_by(name: '宮嶋勇弥')
end

ユーザーオブジェクトを取得したら、view側で「オブジェクト.関連名」で関連するオブジェクトを取得する事が出来ます。

view側 | ユーザーの持つaccount_nameを表示する
1
2
<h2><%= @user.name %></h2>
<p><% @user.account.account_name %></p>

has_oneメソッドで定義したアソシエーションによって、ユーザーに関連するアカウントは1つだけなので、「@user.account」@userの持つアカウントを取得することが出来ます。今回はaccount_nameを表示したいので、「@user.account.account_name」の様に記述します。

アソシエーションで利用可能なオプション
リンクをコピーしました

基本的なテーブルを作成し命名規則に従っていればオプションを利用する事は少ないですが、独自の命名を行っている場合にアソシエーションのオプションを利用する事でカスタマイズする事が出来ます。

オプションの種類は沢山ありますが、belongs_toメソッド、has_oneメソッド、has_manyメソッドで利用することが出来る主要なオプションについて解説していきます。

また、オプションの解説では、has_oneメソッドでも使ったusersテーブルとaccountsテーブルを参考にしていきます。

accountsテーブルとusersテーブル

この関係をアソシエーション定義すると下記の通りでした。

user.rb | UserモデルとAccountモデルの関連付け
1
2
3
4
5
6
7
8
9
# user.rb
class User < ActiveRecord::Base
    has_one :account # 単数形
end

# account.rb
class Account < ActiveRecord::Base
    belongs_to :user 
end

foreign_keyオプション
リンクをコピーしました

foreign_keyとは、外部キー制約のカラム名を指定することが出来るオプションです。

通常は、外部キー制約のカラム名を「user_id」の様に「参照先のモデル名(小文字) 」+ 「_id」の形式になり外部キーだと認識されます。しかし、「参照先のモデル名(小文字) 」+ 「_id」の形式ではなく、違う名前の形式で外部キーに名前をつける場合は、foreign_keyオプションで明示的に宣言する必要があります。

foreign_keyオプションは、下記の様に定義します。

モデルクラス | foreign_keyオプションを定義する
1
2
3
class モデル名 < ActiveRecord::Base
    アソシエーション :関連名, foreign_key: '外部キー名'
end

先ほどの関連では、usersテーブルを参照するaccoutsテーブルの外部キー制約のカラム名は、「user_id」になりますが、これを「user_no」に変更したい場合は、下記の様に定義します。

user.rb | foreign_keyオプションを追加
1
2
3
class User < ActiveRecord::Base
    has_one :account, foreign_key: 'user_no' # 追加
end

class_nameオプション
リンクをコピーしました

class_nameとは、関連名を変更する為に、クラス名を明示的に宣言するオプションです。

関連名とは、関連するテーブルを参照する際のメソッド名になります。このメソッド名を変更するためには、class_nameオプションで関連するモデルのクラス名を指定する必要があります。

class_nameオプションは、下記の様に定義します。

モデルクラス | class_nameオプションを定義する
1
2
3
class モデル名 < ActiveRecord::Base
    アソシエーション :変更した関連名, class_name: '関連するモデルのクラス名'
end

usersテーブルに関連するaccountsテーブルのデータを参照するには、下記の「@user.account」の様にUserモデルのhas_oneメソッドで指定した関連名で参照することが出来ています。

controller側
1
@user = User.find(1)
view側
1
<p><%= @user.account %><p>

「@user.account」で関連するaccountsテーブルのデータを参照していましたが、「@user.pikawaka_account」で参照したい場合は、下記の様に定義します。

user.rb | UserモデルとAccountモデルの関連付け
1
2
3
class User < ActiveRecord::Base
    has_one :pikawaka_account, class_name: 'Account'
end

これによって、関連するアカウントを「@user.pikawaka_account」でaccountsテーブルに参照することが出来ます。

view側 | pikawaka_accountで関連するaccountsテーブルを参照
1
<p><%= @user.pikawaka_account %><p>

dependentオプション
リンクをコピーしました

dependentとは、モデルを削除する時に関連付けされているモデルの挙動を決めるオプションです。

dependnetオプションの主な種類は、下記の通りです。

オプションの種類 説明 指定したモデルの削除時の挙動 補足説明
:destroy 関連するレコードも一緒に削除する destoryを実行 関連するモデルのコールバックが実行される
:delete 関連するレコードも一緒に削除する deleteを実行 直接データベースから削除されるので、コールバックは実行されない
:delete_all 関連するレコードも一緒に削除する deleteを実行 :deleteと挙動は同じだが、has_manyの場合は、:delete_allを使う
:nullify 関連するレコードを削除しない 外部キーにnullが入れられる nullが入る事によって、レコードを残しつつ関連は解消させる。コールバックは実行されない

:deleteと:destroyは、削除時にそれぞれdestoryメソッドとdeleteメソッドが実行されます。この2つのメソッドは、レコードを削除する際に、モデル(ActiveRecord)を介すか介さないかの挙動の違いがあります。詳しくは、destoryとdeleteの違いとは?を参考にしてください。

dependentオプションは、下記の様に定義します。

モデルクラス | dependentオプションの定義する
1
2
3
class モデル名 < ActiveRecord::Base
    アソシエーション :関連名, dependent: :オプションの種類
end

ユーザーを削除する際に、ユーザーに紐づくアカウントも一緒に削除する場合は下記の様に定義します。

user.rb | dependentオプションの定義する
1
2
3
class User < ActiveRecord::Base
    has_one :account, dependent: :destory
end

特にモデルで指定しているコールバックを使わない様でしたら、必要ないクエリが走る事になるので:deleteの方を指定してあげましょう。

まとめ
  • アソシエーションとは、テーブル同士の関連付けをモデル上の関係として操作出来るようにする仕組みのことです。
  • belongs_toメソッドは、参照元テーブル(外部キーを持つテーブル)から参照先テーブル(主キーを持つテーブル)にアクセスする場合に定義します。
  • 1:nの関係は、has_manyメソッドを使って定義します。
  • お互いに1つに対して複数存在している関係を多対多の関係は、has_manyメソッドのthroughオプションを使って定義します。