🎋

accepts_nested_attributes_for の update_only: true を使うべき場面

に公開

背景

コードレビューで accepts_nested_attributes_forupdate_only: true を付与する際の意図について質問があり、理解を整理しました。
1対1のアソシエーション(has_one)でこのオプションを使う場面についての知見をまとめます。

update_only オプションとは

https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html#:~:text=to-many associations.-,%3Aupdate_only,-For a one
accepts_nested_attributes_forupdate_only は、1対1のアソシエーションにのみ使用可能なオプションです。

update_only: true の挙動

ネストされた属性に id があるかどうかにかかわらず、以下の挙動になります。

既存レコードの有無 挙動
あり 更新
なし 新規作成

update_only: false(デフォルト)の挙動

ネストされた属性に id があるかどうかで挙動が変わります。

id 既存レコードの有無 挙動
あり あり 更新
なし あり 新規作成(既存は置き換えられる)
なし なし 新規作成

どういう問題が起きるか

update_only: false(デフォルト)のまま使っていると、id なし かつ 既存のレコードあり の場合に「新規作成して古いレコードとの関連を切る」という挙動になります。

具体的には、古いレコードの外部キーが null になるような動きになると考えられます。
外部キーに NOT NULL 制約が付いているテーブルであれば、この時点でエラーが発生します。

has_one で管理しているような設定系のレコードは、フロント側でフォームの更新処理として id を意識せずに送ることが多いため、このケースに意図せずはまってしまう可能性があります。

まとめ

1対1のアソシエーションで accepts_nested_attributes_for を使う場合、特別な理由がない限りは update_only: true を付与しておくとよさそうです。

id なし かつ 既存レコードあり のケースで「更新」が直感的な挙動であることがほとんどだと思いますし、外部キー制約によるエラーという予期せぬ問題も防ぐことができます。

Discussion