Hisakeyのブログ

エンジニアが色々呟くブログです。

Railsの belongs_toと optional: true の動作について

概要

Rails の belongs_to アソシエーションにおいて、optional: trueを設定するかで、関連するオブジェクトのクエリ発行に違いがあることを知ることができたので、記事にしてみました。

内容

通常、belongs_to を設定すると、その関連付けはデフォルトで必須(required)として扱われます。たとえば、Post モデルが User モデルに関連付けられている場合、以下のように定義します。

class Post < ApplicationRecord
  belongs_to :user
end

このとき、Post インスタンスに対して valid? メソッドなどを呼び出すと、Rails は内部的に関連する User が存在しているかを確認するためにクエリを発行します。これは、外部キーが必須であるため、関連オブジェクトが確実に存在する必要があるからです。

optional: true の設定

関連付けが必須ではない場合、optional: true を指定することができます。

class Post < ApplicationRecord
  belongs_to :user, optional: true
end

optional: true を設定すると、Post インスタンスUser に関連付けられていなくても(user_idnil でも)、バリデーションエラーにはなりません。さらに、valid? のようなメソッドを呼び出したときに、関連する User を取得するクエリも発行されなくなります。

なぜクエリが発行されるのか?

optional: true が設定されていない場合、Postインスタンスが検証されるときに、Rails は関連する User が存在するかどうかをチェックするためにクエリを発行します。これにより、外部キーが存在していても、実際にその ID に対応するレコードが存在しない場合のエラーを防ぐことができます。

しかし、optional: true を設定すると、このチェックがスキップされ、関連するオブジェクトが nil であってもバリデーションが通るため、クエリの発行が不要になります。

まとめ

  • belongs_to のデフォルトの動作:関連オブジェクトが必須であるため、関連オブジェクトの存在を確認するクエリが発行される。
  • optional: true の場合:関連オブジェクトが必須ではなくなるため、valid? のようなメソッドでクエリが発行されることがなくなる。

これにより、関連オブジェクトが存在しない場合にクエリが発行されないようにしたいときは、optional: true を活用するのが効果的です。

以下のコードで動作の違いを確認できます。

# optional: true を指定しない場合
post = Post.new(user_id: 1)
post.valid? # => User を検索するクエリが発行される

# optional: true を指定した場合
class Post < ApplicationRecord
  belongs_to :user, optional: true
end

post = Post.new(user_id: 1)
post.valid? # => User を検索するクエリは発行されない

注意点

optional: trueは、アプリケーションレベルで関連付けを必須としない場合ということを忘れないように気をつけたほうが良さそう

外部キー制約を設けている場合は、アプリケーションレベルで関連付けは任意(optional: true)としながらも、データベースでは関連付けは必須となります。

参考

api.rubyonrails.org