🎋
accepts_nested_attributes_for の update_only: true を使うべき場面
背景
コードレビューで accepts_nested_attributes_for に update_only: true を付与する際の意図について質問があり、理解を整理しました。
1対1のアソシエーション(has_one)でこのオプションを使う場面についての知見をまとめます。
update_only オプションとは
accepts_nested_attributes_for の update_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