概要
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_id が nil でも)、バリデーションエラーにはなりません。さらに、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)としながらも、データベースでは関連付けは必須となります。
参考