LoginSignup
1
1

More than 3 years have passed since last update.

[Rails] [親子関係] 親を削除した時、子にある外部キーをnilで登録したい

Last updated at Posted at 2020-12-21

概要

以下の親子関係をreferenceでリレーション構築している際、
親モデルのデータ削除時に、子である商品データは削除せずに外部キーがnilで登録されるようにしたいがコンソールから親を削除した際にエラーが出て対処方法がわからず少し困った話です。
・親モデル:"カテゴリー"
・子モデル:"商品"

20201211121059_create_products.rb
  t.references :category, null: false, foreign_key: true

実践

以下の記事などを参考に実施した。
optional: trueを設定すれば、nilを許可する方法
https://techtechmedia.com/optional-true-rails/#optional_true
https://qiita.com/takuyanin/items/6f6be86d1265be21bf9e

こんな感じでhas_many, belongs_to にdependent, optionslのオプションを付けた。

catedory.rb
class Category < ApplicationRecord
  has_many :products, dependent: :nullify
end
product.rb
class Product < ApplicationRecord
  belongs_to :category, optional: true
end

この状態で、コンソールから親子のデータを登録。


category = Category.create!(....)
category.products.create!(....)

試しにCategory.destroy_allを実行。子の外部キーがnil登録されるんだよね?
以下のエラーが出て、対処法に困る。

ActiveRecord::NotNullViolation: Mysql2::Error: Column 'category_id' cannot be null

また外部キーnilでの子モデルデータ新規作成時は、optional: trueの設定/未設定でエラーが違う。

※設定時
ActiveRecord::NotNullViolation: Mysql2::Error: Field 'category_id' doesn't have a default value

※未設定時
ActiveRecord::RecordInvalid: Validation failed: Category must exist

そもそもoptional: trueって?
trueにすると外部キーに値が入っていなくても、バリデーションをパスすることができる。
つまり問題はバリデーション
(デフォルトの未設定時はfalseなので、バリデーションを実行する。)

解決へ

(up, downの記法で、rails db:rollbackも可能)

class ChangeReferencesIdToProducts < ActiveRecord::Migration[6.0]
  def up
    change_column :products, :category_id, :bigint, null: true
  end

  def down
    change_column :products, :category_id, :bigint, null: false
  end
end

null: falseが原因だったので、null: trueに変更することで
親を削除しても子の外部キーはnilで登録されるようになりました。

どなたかのお役に立てれば幸いです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1