経緯
いろいろなシステムを構築していると、グループを作成するときにメンバーも同時に作成したくなることがあります。
例えば、以下のようなモデルがあるとして、Group
のnew
のときにMember
も作成することを考えてみます。
class Group < ActiveRecord::Base has_many :members end class Member < ActiveRecord::Base belongs_to :group end
簡単なやり方
gemのインストール
まず、簡単にフォームを扱うために、nested_form
というgemを入れます。
特に、追加できるMember
の数を可変にすることがなければ、このgemは必要ありません。
Gemfile
gem 'nested_form'
$ bundle install
nested_form
はViewで使える便利なHelperを用意してくれます。
Modelの修正
まずはModelを編集します。親のGroup
に対して、
accepts_nested_attributes_for :members, allow_destroy: true
を追記してやりましょう。子モデルのフィールドを扱ってもいいよー、という設定です。
allow_destroy
については後ほど書きます。
Controllerの修正
app/controllers/groups_controller.rb
def new @group = Group.new @group.members.build # 追記 end
Group
を作成するとき、空のGroup
を予め作っておくのと同様に、空のMember
を作成しておいてやります。
app/controllers/groups_controller.rb
def group_params params.require(:group).permit( :name, members_attributes: [:name] # 追記 ) end
members_attributes
はGroup
に紐づくMember
のフィールドになります。
このmembers_attributes
をGroup
自体にぶっこんでやれば、自動的にMember
が作成されるようになります。
Viewの修正
app/controllers/views/groups/new.html.erb
<%= nested_form_for @group do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.fields_for :members do |mf| %> <%= mf.label :name %> <%= mf.text_field :name %> <%= mf.link_to_remove '駆逐してやる!' %> <% end %> <%= f.link_to_add '前方に20m級!' :members %> <%= f.submit %> <% end %>
ひとまず、実行してみると、「前方に20m級!」を押すと、20m級の巨人かどうかは分かりませんが、member
のフィールドが追加され、「駆逐してやる!」を押すと、そのフィールドが削除されることがわかると思います。1
こいつが、nested_form
の便利機能です。
簡単な解説
Viewで、form_for
の代わりにnested_form_for
を使うことで、link_to_add
やlink_to_remove
のようにnested_form
の便利関数を使えるようになりました。
名前からお分かりでしょうが、link_to_add '前方に20m級!', :members
はMember
のフィールドを追加する「前方に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内に記述した
@group.members.build
の記述を
2.times { @group.members.build }
のようにしてやればOKです。
逆に最初からMember
のフィールドを用意しない場合はこの記述自体を削除しましょう。
まとめ
以上のように意外と簡単に作成できるのでいろいろと活用していけると思います。
まとめると、
- 親Modelに子Modelのフィールドへのアクセス許可を与える
- Controllerで予め空の子Modelを作成してやる
- Controllerで
**_attributes
というparamsをModelに渡すように実装する - Viewで
fields_for
を使って子モデルのフィールドを作る
となります。
nested_for
を使うと他にもいくつかカスタマイズも可能なので、それはまたの機会に。
- 進撃の巨人を見てから、大学時代の同級生のデカイやつの後頭部をドーンとやりたい衝動に駆られました。 ↩
コメントを残す