# () <br/> def create<br/> @owner = Owner. new ( params [ :owner ] ) <br/> ...<br/> if @owner . save <br/> @car = Car. new ( params [ :car ] ) <br/> if @car . save <br/> ...<br/> end <br/><br/> # , Rails 2.3+ <br/> def create<br/> @owner = Owner. new ( params [ :owner ] ) <br/> ...<br/> end
また、magicパラメーターのマジックはaccepts_nested_attributes_forで、モデルに書き込まれ、オブジェクトの作成時に追加のパラメーターを渡します。 これらはすべて1行で作成されます。
Owner. create ( :name => "" , :age => 40 , :car_attributes => <br/> { :model => "Formula 1" , :color => "red" } )
したがって、Owner'eと車に関する2つのレコードを一度に作成します。
別の例をより詳細に考えてみましょう。Personモデルはそれ自体と接続する必要があります-人を追加するときに、このモデルを参照する「子を追加する」ことができます。
ステップ1:ネストされた属性を使用したモデルのレポート
関連付けのために最初に行うことは、 accepts_nested_attributes_forの行を追加することです
lass Person < ActiveRecord::Base <br/> validates_presence_of :name <br/> has_many :children , :class_name => 'Person' <br/> accepts_nested_attributes_for :children , :allow_destroy => true <br/> # has_one <br/> end
その後、オブジェクトを使用して子リンクを直接作成、編集、削除できます。
# : <br/> @person . children_attributes = [ { :name => 'Son' } ] <br/> @person . children #=> [ <#Person: name: 'Son'> ] <br/> @person . children . clear <br/> # : <br/> @person . children_attributes =<br/> [ { :name => 'Son' } , { :name => 'Daughter' } ] <br/> @person . save <br/> @person . children #=> [ <#Person: name: 'Son'>, <#Person: name: 'Daughter'> ] <br/> # ( id == 1) <br/> @person . children_attributes = [ { :id => 1 , :name => 'Lad' } ] <br/> @person . save <br/> #=> 'Lad' <br/> # (id == 2) : <br/> @person . children_attributes =<br/> [ { :id => 2 , :name => 'Lassie' } , { :name => 'Pat' } ] <br/> @person . save <br/> #=> 'Lassie', 'Pat' <br/> # Pat'a (id = 3), <br/> @person . children_attributes = [ :id => 3 , '_destroy' => '1' } ] <br/> @person . save <br/> #=> Pat
オブジェクトの作成と編集をサポートするには、1対多の関連付けにハッシュの配列を使用するか、1対1の関連付けにハッシュを使用する必要があります。 ハッシュパラメータ
:id
指定されていない場合、新しいオブジェクトが作成されます。
既存のリンクされたオブジェクトを削除するには、次のメソッドを使用します:
[ { :id => pk, '_destroy' => '1' } ]
、パラメーター '_destroy'をtrueに設定する必要があります。 モデルでオプション
:allow_destroy
を設定することを忘れないでください。デフォルトではオフになっています。
Rails 2.3.5では、_destroy関数の名前が変更されました 。 以前は_deleteと呼ばれていました 。 古いバージョンを使用している場合は、このことを忘れないでください。
おそらくこれは小さなハックのように見えるかもしれませんが、すぐにマルチモデルフォームでの作業が実際に簡素化されることがわかります。
ステップ2.ネストされたモデルを持つフォームを作成する
ビューで、fields_forを追加するだけで、このモデルのフィールドを記述します。
<% form_for @person do | person_form | %> <br/> <% = person_form. label :name %> <br/> <% = person_form. text_field :name %> <br/> <% person_form. fields_for :children do | child_form | %> <br/> <% = child_form. label :name %> <br/> <% = child_form. text_field :name %> <br/> <% unless child_form. object . new_record ? %> <br/> <% = child_form. check_box '_destroy' %> <br/> <% = child_form. label '_destroy' , 'Remove' %> <br/> <% end %> <br/> <% end %> <br/> <% = submit_tag %> <br/> <% end %>
コードは、RESTfulコントローラーに送られるすべての必要なフィールドを持つフォームを作成し、そこからモデルへのchildren_attributesのパラメーターがシームレスに渡されます。 「子供」の作成中にエラーが発生した場合、それらはperson .errorsに追加されます。この場合、オブジェクトはデータベースに保存されません。
いくつかの便利なメモ:
- fields_forを接続
:has_many
使用する場合、関連付けによってリンクされている複数のネストされたモデルを意図せずに相互に追加することにより、再帰の犠牲になる - ネストされたモデルのオブジェクト内の何かを変更する必要がある場合は、 child_form.objectを使用してアクセスできます。 上記の例では、 child_form.object.new_record?を使用しましたか? 「削除」チェックボックスを表示するかどうかを決定します(新しいエントリには必要ありません)
ステップ3.コントローラーに登録するものは?..何もない
3番目のステップはおそらく最も簡単です。なぜなら、RESTを壊すことなくすべてを行うからです。 このソリューションの利点は、モデル内に不要なコードがコントローラーに散らばっていないことです。 これらの作成および更新メソッドを見てください:
class PersonController < ApplicationController<br/> def create<br/> @person = Person. new ( params [ :person ] ) <br/> @person . save ? redirect_to ( person_path ( @person ) ) : render ( :action => :new ) <br/> end <br/> def update <br/> @person = Person. find ( params [ :id ] ) <br/> @person . update_attributes ( params [ :person ] ) ?<br/> redirect_to ( person_path ( @person ) ) : render ( :action => :edit ) <br/> end <br/> end
ご覧のとおり、コントローラーでの不必要な処理を行うことなく、モデルおよびフォームで指定したすべてが機能します。
オプショナル
ネストされたフィールドをすべて表示
多くの場合、ネストされたフィールドはすぐに表示する必要があります。 たとえば、ユーザーが新しい人を作成すると同時に、子を追加する場合。
作成されたオブジェクトpersonが作成されたばかりであるため、child_formフィールドは表示されません。 この問題を解決するには2つの方法があります。
-コントローラーで新しいオブジェクトを作成します。
def new <br/> @person = Person. new <br/> @person . children . build <br/> # ... <br/> end
-ほとんど同じことを行うが、別の場所にあるヘルパーを追加できます。
module ApplicationHelper<br/> def setup_person ( person ) <br/> returning ( person ) do | p | <br/> p . children . build if p . children . empty ?<br/> end <br/> end <br/> end
その後、 form_for personをわずかに異なるものに変更します。
<% form_for setup_person ( @person ) do | person_form | %> <br/> <!-- ... --><br/> <% end %>
これらの各方法を使用するには、誰もが自分で決定します。 個人的には、最初の記事、元の記事の著者-2番目の記事を好みます。
ネストされたモデルが必要になるタイミングを指定する
デフォルトでネストされたフィールド(Person→children)があるフォームを作成すると、だれかが空のパラメーター(子なし)でフォームを送信しようとします。 ユーザーにエラーを表示させることができます-子供たちが記入して戻る必要がある場合。 または、子のないオブジェクトを作成します。
これにはオプションがあります
:reject_if
:
class Person < ActiveRecord::Base <br/> validates_presence_of :name <br/> has_many :children , :class_name => 'Person' <br/> # , <br/> accepts_nested_attributes_for :children ,<br/> :reject_if => proc { | attrs | attrs. all ? { | k, v | v. blank ? } } <br/> # , <br/> accepts_nested_attributes_for :children , :reject_if => :all_blank <br/> end
# <br/> @person . children_attributes = [ { :name => '' } ] <br/> @person . save <br/> @person . children . count #=> 0
このオプションは、モデルにブールフィールドがあり、フォームにチェックボックスがある場合にも役立ちます。 マークせず、子の名前を書き込まない場合、パラメーター「0」のみがコントローラーに転送され、
:all_blank
なくなります
class Person < ActiveRecord::Base <br/> validates_presence_of :name , :bad <br/> has_many :children , :class_name => 'Person' <br/> # , , <br/> accepts_nested_attributes_for :children ,<br/> :reject_if => proc { | attrs | attrs [ 'bad' ] == '0' && attrs [ 'name' ] . blank ? } <br/> <br/> # :reject_if => proc { |attrs| attrs['name'].blank? } <br/> # , (. .) <br/> end
@person . children_attributes = [ { :name => '' , :bad => '0' } ] <br/> @person . save <br/> @person . children . count #=> 0
動的にロードされるフィールド
多数のモデルが存在する複雑なフォームを構築し、さらにいくつかのレベルでネストされている場合、デフォルトでいくつかのネストされたフォームを表示できます。 これは完全に合理的ではありませんが。 より魅力的なオプションは、ユーザーのリクエストでJavaScriptを使用して新しいフィールドを動的に追加することです。
Eloyが作成したGitHubで優れたサンプルアプリケーションを表示できます。 これを見ると、モデルのシステム全体とモデル間の関係がどのように機能するかを理解できます。