経緯

いろいろなシステムを構築していると、グループを作成するときにメンバーも同時に作成したくなることがあります。

例えば、以下のようなモデルがあるとして、GroupnewのときにMemberも作成することを考えてみます。

簡単なやり方

gemのインストール

まず、簡単にフォームを扱うために、nested_formというgemを入れます。
特に、追加できるMemberの数を可変にすることがなければ、このgemは必要ありません。

Gemfile

nested_formはViewで使える便利なHelperを用意してくれます。

Modelの修正

まずはModelを編集します。親のGroupに対して、

を追記してやりましょう。子モデルのフィールドを扱ってもいいよー、という設定です。
allow_destroyについては後ほど書きます。

Controllerの修正

app/controllers/groups_controller.rb

Groupを作成するとき、空のGroupを予め作っておくのと同様に、空のMemberを作成しておいてやります。

app/controllers/groups_controller.rb

members_attributesGroupに紐づくMemberのフィールドになります。
このmembers_attributesGroup自体にぶっこんでやれば、自動的にMemberが作成されるようになります。

Viewの修正

app/controllers/views/groups/new.html.erb

ひとまず、実行してみると、「前方に20m級!」を押すと、20m級の巨人かどうかは分かりませんが、memberのフィールドが追加され、「駆逐してやる!」を押すと、そのフィールドが削除されることがわかると思います。1
こいつが、nested_formの便利機能です。

簡単な解説

Viewで、form_forの代わりにnested_form_forを使うことで、link_to_addlink_to_removeのようにnested_formの便利関数を使えるようになりました。
名前からお分かりでしょうが、link_to_add '前方に20m級!', :membersMemberのフィールドを追加する「前方に20m級!」というボタンを作成し、
link_to_remove '駆逐してやる!'Memberを削除する「駆逐してやる!」というボタンを作ってくれます。
ちなみに、link_to_removeは先ほど、Modelのところで、少し触れたallow_destroy: trueがないと動きません。ご注意ください。
nested_formを使わないときはこれらの機能が使えないので、上記サンプルからは削除しましょう。

f.fields_for :membersはこのBlock内は@groupに紐付いたmemberのフィールドですよ!といっています。
出力されるHTMLを確認してみればわかりますが、この中のフィールドのnameにControllerで定義したmembers_attributesが含まれています。
mfという変数を使うこと以外は通常のform_forの中と同じように記述できます。

最初に2つ以上のフィールドを作っておきたい。

最初に作っておきたいフィールドの数を変更したいときはController内に記述した

の記述を

のようにしてやればOKです。

逆に最初からMemberのフィールドを用意しない場合はこの記述自体を削除しましょう。

まとめ

以上のように意外と簡単に作成できるのでいろいろと活用していけると思います。
まとめると、

  • 親Modelに子Modelのフィールドへのアクセス許可を与える
  • Controllerで予め空の子Modelを作成してやる
  • Controllerで**_attributesというparamsをModelに渡すように実装する
  • Viewでfields_forを使って子モデルのフィールドを作る

となります。

nested_forを使うと他にもいくつかカスタマイズも可能なので、それはまたの機会に。


  1. 進撃の巨人を見てから、大学時代の同級生のデカイやつの後頭部をドーンとやりたい衝動に駆られました。