optional 関連先の存在チェックをしない
使用可能な関連付け
belongs_to
対となる関連に指定するオプション
なし
概要
以下のようにした場合、関連先のauthorは必須になる。
class Book < ApplicationRecord
belongs_to :author
end
そのため、以下のvalid?は全てfalseになる。
Book.new.valid?
Book.new(author_id: -1).valid?
Book.new(author_id: Author.last.id + 1).valid?
以下のような実際のauthorに関連している場合だけtrueになる。
Book.new(author_id: Author.last.id).valid?
Book.new(author: Author.last).valid?
ただし、後者の場合は渡されたauthorのレコードが削除されていてもvalid?はtrueになるので注意が必要。books.author_idに外部キー制約がされていればActiveRecord::InvalidForeignKey例外が投げられるが、外部キー制約がされていなければ、そのまま保存されてしまう。
このように関連先が必須というのがデフォルトの挙動だが、optional: trueを指定することで関連先の有無を任意にできる。
belongs_toの定義を以下のように変える。
class Book < ApplicationRecord
belongs_to :author, optional: true
end
すると、以下のvalid?もtrueを返すようになる。
Book.new.valid?
Book.new(author_id: -1).valid?
Book.new(author_id: Author.last.id + 1).valid?
ただし、NOT NULL制約がされていれば最初の例が、外部キー制約がされていれば残り2つの例がsaveを実行したタイミングで例外を投げられるため保存することができない。
validatesによる再現
以下の3つは同じ挙動をする(厳密にはvalidatesを使った場合はメッセージが異なる)。
belongs_to :author
belongs_to :author, optional: false
belongs_to :author, optional: true
validates :author, presence: true
以下の例の場合は明確に挙動が異なる。
belongs_to :author, optional: true
validates :author_id, presence: true
この例の場合、author_idがnil以外ならバリデーションエラーにならない。先の3つの例はnilだけでなく実際に関連先が存在することがチェックされる。
config.active_record.belongs_to_required_by_default
Railsの設定項目にoptionalに関するものがある。
元々、belongs_to :authorと書いた場合にはbelongs_to :author, optional: trueと書いた時と同じ挙動をしていた(当時、optionalという設定項目があったかは調べていない)。これがRails 5.0以降はbelongs_to :author, optional: falseと記述した時と同じ挙動に変更されている。
過去の挙動を維持したい場合は、config/application.rbに以下を追加すれば良い。
# load_defaultsよりもあとに追加
config.active_record.belongs_to_required_by_default = false
ただし、この設定をfalseにした場合、bin/rails g model book author:belongs_toなどとした時の外部キーにNOT NULL制約が自動では付かなくなる。