Railsのhas_manyのdependentにoptionついて

✏️️ 2019/08/22 👍️2019/08/22 🔗

has_many アソシエーションで利用するdependentについて纏めました。

前提

Category has_many posts. の関係とします。記事(Post Model)はカテゴリー(Category Model)に属してます。

# app/models/post.rb
class Post < ApplicationRecord
   belongs_to :category
end
# app/models/category.rb
class Category < ApplicationRecord
  has_many :posts
end

dependent optionについて

dependent オプションは以下の5つです。

option 意味
:destroy 関連するオブジェクトもすべて破棄されます
:delete_all 関連するすべてのオブジェクトがデータベースからActive Recordを介さず直接削除されます。(コールバックは実行されません)
:nullify 関連するオブジェクトにnullをセットします
:restrict_with_exception 関連するオブジェクトがある場合ActiveRecord::DeleteRestrictionErrorを発生させます
:restrict_with_error 関連するオブジェクトがある場合Errorになります

以下、記事に紐付いているカテゴリーを削除する場合の例です。

dependent オプションなし

# app/models/category.rb
class Category < ApplicationRecord
  has_many :posts
end

Errorになります。

> post.category.destroy
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR:

dependent: :destroy

# app/models/category.rb
class Category < ApplicationRecord
  has_many :posts, dependent: :destroy
end

CategoryとPostが削除されます。つまり、関連するオブジェクトもすべて破棄されます。

> post.category.destroy
Post Destroy (1.1ms)  DELETE FROM "posts" WHERE "posts"."id" = $1  [["id", 16]]
Category Destroy (0.4ms)  DELETE FROM "categories" WHERE "categories"."id" = $1  [["id", 4]]

dependent: :delete_all

# app/models/category.rb
class Category < ApplicationRecord
  has_many :posts, dependent: :delete_all
end

CategoryとPostが削除されます。関連するすべてのオブジェクトがデータベースから直接削除されます(コールバックは実行されません)。

> post.category.destroy
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR:

> category.destroy
  Category Load (0.2ms)  SELECT "categories".* FROM "categories" ORDER BY "categories"."id" DESC LIMIT $1  [["LIMIT", 1]] (0.1ms)  BEGIN
  Post Destroy (0.2ms)  DELETE FROM "posts" WHERE "posts"."category_id" = $1  [["category_id", 6]]
  Category Destroy (0.4ms)  DELETE FROM "categories" WHERE "categories"."id" = $1  [["id", 6]]
   (0.4ms)  COMMIT

dependent: : nullify

# app/models/category.rb
class Category < ApplicationRecord
  has_many :posts, dependent: : nullify
end

Categoryは削除されますがPostは削除されません。

> post.category.destroy
Post Update All (0.7ms)  UPDATE "posts" SET "category_id" = $1 WHERE "posts"."category_id" = $2  [["category_id", nil], ["category_id", 5]]
Category Destroy (0.6ms)  DELETE FROM "categories" WHERE "categories"."id" = $1  [["id", 5]] (0.5ms)  COMMIT

dependent: :restrict_with_exception

# app/models/category.rb
class Category < ApplicationRecord
  has_many :posts, dependent: :restrict_with_exception
end

Categoryは削除とPostは削除されません。ActiveRecord :: DeleteRestrictionError例外が発生します。

> post.category.destroy
ActiveRecord::DeleteRestrictionError: Cannot delete record because of dependent posts

dependent: :restrict_with_error

# app/models/category.rb
class Category < ApplicationRecord
  has_many :posts, dependent: :restrict_with_error
end

Categoryは削除とPostは削除されません。関連するオブジェクトがある場合、エラーになります。

> post.category.destroy
=> false

参考

Akito
日本のスタートアップで主にRuby on Railsを使ってプロダクト開発をしています。