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_idnil以外ならバリデーションエラーにならない。先の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制約が自動では付かなくなる。