マルチモデルフォームの作成

データが複数のテーブルに関連付けられているフォームを作成する必要がある場合があります。 たとえば、所有者と車という2つのモデルがあります。 新しいオーナーを追加するときに、すぐに車を追加できるようにしたいと思います。 Rails 2.3の登場により、これはより簡単になりました。



# () <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に追加されます。この場合、オブジェクトはデータベースに保存されません。



いくつかの便利なメモ:



ステップ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で優れたサンプルアプリケーションを表示できます。 これを見ると、モデルのシステム全体とモデル間の関係がどのように機能するかを理解できます。



All Articles