はじめに
Railsで親子関係のあるデータ(例:会員と「好きなもの」「嫌いなもの」)を扱う際、親レコードの更新と一緒に子レコードも一括で保存・削除したいケースは頻繁にあります。
この記事では、Railsの accepts_nested_attributes_for を使用して、画面上での「更新・新規作成・削除」をDBへ正確に反映させる仕組みと、React側でのデータ構築方法について解説します。
この記事でわかる・できること
- nested attributes を使った子レコード削除(_destroy)の仕組み
- 親子関係のデータを1回のリクエストで安全に整合性を保って更新する方法
- フロントエンド(React)からRailsへ送るべきデータ構造の具体的な実装
この記事の対象者
- Railsで親子関係のフォーム作成に悩んでいる方
- 画面で削除した項目がDBから消えずに困っている方
- ReactなどのSPAからRailsの nested attributes を利用したい方
動作環境・使用するツールや言語
- Rails: 7.0以上
- Ruby: 3.2以上
- React: 18以上
1. nested attributes とは
Railsには、親モデルを通じて子モデルを保存・更新する機能があります。これを nested attributes と呼びます。
基本的な構造
例えば Member(会員)が複数の Like(好きなもの)を持っている場合:
Member
├─ likes(ラーメン、カレー)
└─ dislikes(パクチー)
通常はそれぞれ個別に保存処理が必要ですが、この機能を使うと Member の保存時に likes や dislikes もまとめて処理できるようになります。
Rails側の設定
モデルに accepts_nested_attributes_for を定義し、allow_destroy: true を付けることが重要です。
これがないと削除機能が動作しません。
class Member < ApplicationRecord
has_many :likes, dependent: :destroy
has_many :dislikes, dependent: :destroy
# allow_destroy: true が削除処理に必須
accepts_nested_attributes_for :likes, allow_destroy: true
accepts_nested_attributes_for :dislikes, allow_destroy: true
end
2. 子レコード削除の仕組み(_destroy)
画面上で「削除」ボタンを押したとき、単にフォームから消すだけではDB上のレコードは消えません。Railsに「削除命令」を送る必要があります。
削除に必要なデータ形式
削除したいレコードに対して、以下の形式でパラメータを送ります。
likes_attributes: [
{ id: 5, _destroy: true }
]
- id: どのレコードか特定する
- _destroy: true: このレコードを削除するという命令
これにより、「画面上の削除」と「DB上の削除」が一致します。
3. React側でのデータ構築ロジック
ここからは、フロントエンド(React)からRails APIへリクエストを送る際の、データ構築の実装例です。
「更新」「新規」「削除」の3つの状態を、1つの配列(attributes)にまとめるのがポイントです。
実装コード(React)
const likesAttrs = [
// 1. 更新または新規作成の処理
...editLikes.map((like) =>
like.id
? { id: like.id, name: like.name } // IDがある = 既存レコードの更新
: { name: like.name } // IDがない = 新規作成
),
// 2. 削除の処理
...removedLikeIds.map((id) => ({ id, _destroy: true })),
];
ロジックの分解解説
① 更新と新規の判別
editLikes(画面に残っているリスト)をマップします。
- id がある場合: { id: 5, name: "ラーメン" }
- Railsは「ID:5 のレコード名を更新」と判断します。
- id がない場合: { name: "カレー" }
- Railsは「新しいレコードを作成」と判断します。
② 削除データの追加
removedLikeIds(削除ボタンが押されたIDリスト)をマップします。
- 削除対象: { id: 7, _destroy: true }
- Railsは「ID:7 のレコードを削除」と判断します。
完成するパラメータのイメージ
上記ロジックを通すと、Railsには以下のような配列が届きます。
likes_attributes: [
{ id: 5, name: "ラーメン" }, // 更新
{ name: "カレー" }, // 新規作成
{ id: 7, _destroy: true } // 削除
]
まとめ
nested attributes を使いこなすことで、複雑な親子関係のデータ操作をシンプルかつ安全に行うことができます。
- Rails側: accepts_nested_attributes_for に allow_destroy: true をつける
- 削除の仕組み: 対象の id と _destroy: true をセットで送る
- データ構築: 「更新・新規・削除」を1つの配列にまとめてAPIへ送信する
この設計パターンは、Railsフォーム設計の核心部分であり、API開発においても非常に有効な手段です。