今回は外部キーのバリデーションチェックを回避するoptional: true
について簡単にまとめたので解説したいと思います。通常(optional: true
を記載しない場合では)ではoptional: false
になっているとのことですが、trueとfalseではどのような違いがあるのか見ていきましょう。
optional: trueとは
optional: true
とは、アソシエーションによって紐づけられた外部キーの値が存在しない値やnilの場合でも、データベースに保存することができるオプションです。通常、外部キーの値が存在しない値やnilであればバリデーションで弾かれてしまいます。
例えば、ItemモデルとImageモデルが1対多の関係でアソシエーションが組まれているとします。(has_many :images
とbelongs_to :item
)
# models/item.rb
class Item < ApplicationRecord
has_many :images
end
# models/image.rb
class Image < ApplicationRecord
belongs_to :item
end
そしてimagesテーブルに外部キーとしてitem_id
が格納されています。
ActiveRecord::Schema.define(version: 2021_12_10_024146) do
create_table "images", charset: "utf8mb4", do |t|
t.string "url", default: "", null: false
t.bigint "item_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
ここでimagesテーブルに存在しないitem_id
と一緒にurl
を保存してみます。
しかし、create処理がバリデーションによって弾かれ、以下のように「Item must exist」というエラーが吐き出されてしまいました。
# item_id: 1は存在しない
Image.create!(url: 'https://sample.com', item_id: 1)
=> ActiveRecord::RecordInvalid: Validation failed: Item must exist
これが通常の挙動、optional: false
の挙動になります。通常では、外部キーに存在する値が入っていないとバリデーションで弾かれてしまいます。
一方でImageモデルにoptional: true
を記載するとどうなるのでしょうか。
# models/image.rb
class Image < ApplicationRecord
belongs_to :item, optional: true
end
# item_id: 1は存在しない
Image.create!(url: 'https://sample.com', item_id: 1)
=> <Image:0x00007fc437abf038
id: 1,
url: 'https://sample.com',
item_id: 1,
created_at: Sun, 12 Dec 2021 10:06:52 UTC +00:00,
updated_at: Sun, 12 Dec 2021 10:06:52 UTC +00:00>
create処理が成功し、存在しないitem_id
が保存されていますね。
このようにoptional: true
は、外部キーが存在しない値やnilの場合でもバリデーションに弾かれずに保存処理ができるオプションになります。
まとめ
optional: true
とは、アソシエーションによって紐づけられた外部キーの値が存在しない場合でも、データベースに保存することができるオプション- 外部キーがないと弾かれてしまう挙動はRails5以降で導入されている
- 記述方法としては、
belongs_to
の後にoptional: true
を記述する
参考記事
- belongs_to should default to required: true
- RailsガイドActive Record の関連付け
- ActiveRecord::Associations::ClassMethods
今回は外部キーのバリデーションチェックを回避するoptional: true
について簡単に解説しました。中間テーブルに値を保存する際など、やむを得ずアソシエーション先のバリデーションチェックを一時回避したい場合はoptional: true
をつけて回避してあげてください。