2013-07-13 5 views
14

私は比較的新しいレールで、最終的にはaccepts_nested_attributes_forを使用する正しい方法を見つけました。accepts_nested_attributes_forの代わりに - maybe virtus

しかし、を使用することは一般的に悪いことです(このようなone)。 accepts_nested_attributes_forを回避するために必要であり、そのフォルダ内に追加のクラスファイルを(私は1つの追加のクラスを必要と推測)入れてしまうでしょうどのような変更

私はそれにはvirtusが適切であると読んでいます。そうですか?

モデル

class Person < ActiveRecord::Base 

    has_many :phones 
    accepts_nested_attributes_for :phones 

end 

class Phone < ActiveRecord::Base 

    belongs_to :person 

end 

コントローラ

class PeopleController < ApplicationController 

    def new 

     @person = Person.new 
     @person.phones.new 

    end 

    def create 

     @person = Person.new(person_params) 
     @person.save 

     redirect_to people_path 

    end 

    def index 

     @people = Person.all 

    end 

private 

    def person_params 

     params.require(:person).permit(:name, phones_attributes: [ :id, :number ]) 

    end 

end 

ビュー(人/:ここ

はまだaccepts_nested_attributes_forを使用して、非常に基本的な例(完全な例hereを見つける。)でありますnew.html.erb)

<%= form_for @person, do |f| %> 
    <p> 
     <%= f.label :name %><br /> 
     <%= f.text_field :name %> 
    </p> 
    <%= f.fields_for :phones do |builder| %> 
    <p> 
      <%= builder.label :number %><br /> 
      <%= builder.text_field :number %> 
    </p> 
    <% end %> 
    <%= f.submit %> 
<% end %> 

[編集]
は、サービスオブジェクトを使用することをお勧めでしょうか?

答えて

18

あなたの質問は、あなたがaccepts_nested_attributes機能が悪いと信じていることを意味しまったく事実ではなく、完全にうまくいくもの。

私はあなたがaccepts_nested_attributes_forする代替は必要ありませんが、私はこの記事の最後でそれをカバーしましょうと言ってから始めましょう。あなたが提供するリンクを参照して

、それはポスターがすべてで非推奨となり、ただ単に私の愚見で

を述べているべきであるaccepts_nested_attributes_forと考えている理由については何も引き合いに出していない、

を廃止されるべきですネストされた属性は、Ruby on Railsだけではなく、ブラウザからサーバーにデータを送信するために最も複雑なWebアプリケーションで使用される、単一フォームで親に関連する複数のレコードをキャプチャする方法を検討する際に非常に重要な概念ですあなたは言語uのサイトを開発するsed。

あなたが指摘している記事を批判しているわけではありません。私にとっては、ビジネスロジックに必ずしも関連していない多くのコードをデータベースバックアップモデルに埋め込むという明白な選択肢を指摘しています。使用される特定の例は、単なるコーディングスタイル優先選択肢である。

時間がお金で、プレッシャーがかかっていて、1行のコードとこの例の22行のコードを比較すると、ほとんどの場合(ほとんどの場合ではない)私の好みでは、1行のコードを使用します。フォームから返信されるネストされた属性を受け入れるモデル(accepts_nested_attributes_for)。

あなたがaccepts_nested_attributes_forが良い習慣ではないと思う理由が実際には述べられていないので、あなたの質問に正しく答えることは不可能です。しかし、最も単純な方法はコントローラのアクションでparamsハッシュ属性を抽出して、

更新 - コメント

のフォローアップ私は、リンク先の記事の著者は OOP-パラダイム以下、すべてのオブジェクトは読み取り専用で、独自のデータを書き込む必要がある、と主張していると思います。 accepts_nested_attributes_forでは、一方のオブジェクトは一部のオブジェクトを変更します。

O.K.それを明らかにする。 まず、OOのパラダイムは、そのようなことを示唆していません。クラスは慎重であるべきですが、他のクラスと対話することは許可されています。実際、Rubyのあらゆる点がクラスであるため、何か他のものと話すことができないので、Rubyではオブジェクト指向のアプローチには何の指摘もありません。ちょうどあなたのコントローラのインスタンスになるオブジェクトがモデルや他のコントローラとやりとりできない場合に起こることを想像してみてください。 accepts_nested_attributes_forで

、一つの目的は、しかし、いくつかの 他のオブジェクトデータを変更します。

これは複雑なものですので、その声明のポイントのいくつかを私は可能な限り簡潔にしようとします。

1)モデルインスタンスがデータを保護します。他のほとんどの言語(C、Delphi、VBなど)に数百のテーブルが含まれている非常に複雑なシナリオでは、3層ソリューションの中間層でも同様です。 Railsという言葉では、モデルはビジネスロジックのための場所であり、中間層の仕事は3階層ソリューションで実行され、通常はRDBMSのストアドプロシージャとビューによってバックアップされます。モデルは互いに正しく話すことができるはずです。

2)accepts_nested_attributes_forは、OOの原則をまったく破りません。メソッドが存在しない場合(見つけている場合)は、書く必要があるコードの量を単純化するだけです。子モデルのためのparamsハッシュの中に入れ子になっている属性を受け入れるならば、子モデルがコントローラの動作と同じ方法でそのデータを扱えるようにするだけです。ビジネスロジックはバイパスされず、追加のメリットが得られます。

最後

私は(時間程度以上)のコードの優雅さを気にする

を買う余裕ができ、私は、コードの20 +以上のラインよりを書くことについて、エレガントなものは何も存在しないことを保証することができます1行のコードで作業を行う宝石から数百行以上のコードを追加する必要があります。他の人が(私を含めて)accepts_nested_attributes_forは必ずしも適切なActiveRecordメソッドではないと述べているので、最終的にはいつビルドを使うべきかについてより良い情報に基づいた判断を下すことができます。あなた自身のことを書いています。しかし、私は、何が起こっているのかを完全に理解するためには、フォームオブジェクトを扱う独自のコードを記述し、ネストされた属性の選択肢を受け入れるほうがよいと言います。そうすれば、あなたはもっと多くのことを理解するでしょう。

あなたの学習には意味があり、幸運になることを望みます。最終的にあなたのポイントに、あなた自身の答えを参照してもらうプラス他はVirtus社の宝石に裏打ちされた独自の回答フォームオブジェクトの上に作られている優れたコメントを考慮し

UPDATE 2

は完璧です特にデータの収集方法を扱う際には合理的な解決策です。この組み合わせは、ユーザーロジックをビジネスロジックから分離するのに役立ちます。そして、最終的にモデルにデータを渡して、ビジネスロジックがバイパスされないようにしている限り(あなたが正確にこれを行っていることを示すように) 。

accepts_nested_attributesを除外しないでください。

また、フォームオブジェクトのRyan Bates氏がrailscastsを見ていると、いくつかの利点があります。

+0

私はあなたの議論を非常によく理解しており、私があなたが記述した状況の下でそれをサポートします。私はプロの開発者ではなく、ホビービストなので、コードの優雅さに気を取る余裕があります。私は、リンクされた記事の著者は、oop-paradigmsに続いて、すべてのオブジェクトが独自のデータを読み書きする必要があると主張していると思います。 accepts_nested_attributes_forでは、一方のオブジェクトは他のオブジェクトデータを変更します。 – speendo

+3

ベストパターンを選ぶことで、長期的にはより多くの時間を節約できます。多くの異なる懸念と責任であなたのモデルを汚染することは後で頭痛を与えるだけです。フォームオブジェクトの使用は、ネストされた属性を使用するよりもはるかに良い方法です。キーは「適切な場所」です。ネストされた属性を使用する方が良い解決策である状況があります。 –

+1

また、なぜ非難は少し極端かもしれませんが、私はまだ、新しい開発者(speendo)に "Railsのやり方"の選択肢を試してみる方が良いと思います。最近、Railsが膨大なアプリケーションでスケールされない(コードベースであり、アプリケーション自体ではない)ことを実感した後、多くの人がフォームオブジェクトなどのものを使い始めたようです。人々がこれをやめないようにすることは、傷つけるだけです.Railsは、フレームワークとして前進し、成熟するより難しい時間を持つでしょう。 –

4

accepts_nested_attributes_forの代わりにvirtusを使用するほうがずっと簡単です。最も重要な要件は、私がまだ読んでいるチュートリアルでカバーされていないものを作ることを敢えてすることでした。ステップバイ

ステップ:

  1. 私はGemfileにgem 'virtus'を追加し、bundle installを走りました。
  2. 私は、ファイルモデル/ contact.rbを書き、次のコードを書いた:その後、私は

    class ContactsController < ApplicationController 
    
        def new 
    
        @contact = Contact.new 
    
        end 
    
        def create 
    
        @contact = Contact.new(contact_params) 
        @contact.save 
        redirect_to people_path 
    
        end 
    
        def contact_params 
    
        params.require(:contact).permit(:name, :number) 
    
        end 
    
    end 
    
  3. で* *モデル/ contacts_controller.rbをrails generate controller contactsを実行し、満たさ

    class Contact 
        include Virtus 
    
        extend ActiveModel::Naming 
        include ActiveModel::Conversion 
        include ActiveModel::Validations 
    
        attr_reader :name 
        attr_reader :number 
    
    
        attribute :name, String 
        attribute :number, Integer 
    
        def persisted? 
        false 
        end 
    
        def save 
        if valid? 
         persist! 
         true 
        else 
         false 
        end 
        end 
    
    private 
    
        def persist! 
        @person = Person.create!(name: name) 
        @phones = @person.phones.create!(number: number) 
        end 
    end 
    
  4. 次のステップはビューでした。私は、私もそれだその経路にresources :contacts

を追加するために必要なもちろん

<%= form_for @contact do |f| %> 
    <p> 
    <%= f.label :name %><br /> 
    <%= f.text_field :name %> 
    </p> 

    <p> 
    <%= f.label :number %><br /> 
    <%= f.text_field :number %> 
    </p> 

    <%= f.submit %> 
<% end %> 
  • ビュー/連絡先/ new.html.erbを作成し、この基本的な形を書きました。たぶんそれはよりエレガントに行えるかもしれません。他のCRUDアクションに対してもコンタクトクラスのみを使用することで支払いを行うこともあります。だから、受け入れ答えはただaccepts_nested_attributes_forは、多くの場合、良い解決策である理由と言うが、実際にどのように上のソリューションを提供していませんhttps://github.com/speendo/PhoneBook/tree/virtus/app/models

  • +4

    ここで重い吊り上げをしているのは、バーテスではありません。フォームオブジェクトを使用しているだけです(リンクしたブログ投稿に記載されています)。 Virtusは、(他の多くのものの中でも)フォームオブジェクトの作成を手助けするライブラリです。 –

    +0

    @LoganSermanもちろん、ビューを作成するのは難しいでしょうか? – speendo

    +1

    これは、 'Contact.new(contact_params)'が可能なように、属性のハッシュでモデルを作成することを可能にします。基本的には、通常のオブジェクト(あなたの 'Contact'フォームオブジェクト)をActiveRecordのように動作させます。 –

    3

    :私はあなたがここにすべての変更を見つけることができる、まだ...

    をしようとしませんでしたそれをやる。フォームに動的な数の入れ子になったオブジェクトを受け入れるようにするには、リンクされたアーティクルの例が問題になります。これが唯一のソリューションです私は後世のため

    https://coderwall.com/p/kvsbfa/nested-forms-with-activemodel-model-objects

    を見つけたこれが基本ですが、サイト上で、もう少しあります:

    class ContactListForm 
    include ActiveModel::Model 
    
    attr_accessor :contacts 
    
    def contacts_attributes=(attributes) 
        @contacts ||= [] 
        attributes.each do |i, contact_params| 
        @contacts.push(Contact.new(contact_params)) 
        end 
    end 
    end 
    
    class ContactsController < ApplicationController 
        def new 
         @contact_list = ContactListForm.new(contacts: [Contact.new]) 
        end 
        end 
    

    f.fields_for :contactshas_manyの関係のように振る舞う必要があり、フォームオブジェクトで簡単に処理できます。

    モデルでない場合は、persisted?を偽装する必要があります。