3

シナリオ:has_manyアソシエーション(投稿には多くの作成者がいます)があり、作成者の属性を受け入れるためのネストされた投稿フォームがあります。

私が見つけたのは、post.update_attributes(params [:post])を呼び出すと、params [:post]はpostと追加するすべての作成者属性を含むハッシュであり、Railsにのみ要求する方法がないようです。特定の基準が満たされている場合、たとえば作成者のユーザー名がすでに存在する場合は、作成者を作成します。Railsが行うことは、usernameがモデルで一意性の検証を行っている場合に、update_attributesルーチンを失敗させてロールバックすることです。そうでない場合、IDを持たないレコード作成者がハッシュに含まれていると、Railsは新しいレコード作成者を追加します。

これで、Postコントローラーの更新アクションのコードは次のようになります。

def update
  @post = Post.find(params[:id])

  # custom code to work around by inspecting the author attributes
  # and pre-inserting the association of existing authors into the testrun's author
  # collection
  params[:post][:authors_attributes].values.each do |author_attribute|
    if author_attribute[:id].nil? and author_attribute[:username].present?
      existing_author = Author.find_by_username(author_attribute[:username])
      if existing_author.present?
        author_attribute[:id] = existing_author.id 
        @testrun.authors << existing_author
      end
    end
  end

  if @post.update_attributes(params[:post])
    flash[:success] = 'great!'
  else
    flash[:error] = 'Urgg!'
  end

  redirect_to ...
end

私が逃したこれを処理するためのより良い方法はありますか?

編集:@ Robd'Apiceに感謝します。私に代わって、accepts_nested_attributes_forがモデルに挿入するデフォルトのauthors_attributes =関数をオーバーライドすることを検討してくれたので、より良いものを思いつくことができました。

def authors_attributes=(authors_attributes)
  authors_attributes.values.each do |author_attributes|
    if author_attributes[:id].nil? and author_attributes[:username].present?
      author = Radar.find_by_username(radar_attributes[:username])
      if author.present?
        author_attributes[:id] = author.id
        self.authors << author
      end
    end
  end
  assign_nested_attributes_for_collection_association(:authors, authors_attributes, mass_assignment_options)
end

しかし、私はそれに完全に満足していません。1つは、呼び出し元からの属性ハッシュを直接マックしているため、これらのハッシュに対してロジックがどのように機能するかを理解する必要があります(たとえば、:idが設定されているかどうか)。 、ここに収まるように簡単ではない関数を呼び出しています。特定の条件が満たされない場合にのみ新しいレコードを作成するように「accepts_nested_attributes_for」に指示する方法があると便利です。1対1の関連付けには、同様のことを行う:update_onlyフラグがありますが、これは1対多の関係にはありません。

そこにもっと良い解決策はありますか?

4

2 に答える 2

1

この種のロジックは、コントローラーではなくモデルに属している可能性があります。author_attributes=関連付け用にデフォルトで作成されるメソッドを書き直すことを検討します。

def authors_attributes=(authors_attributes)
  authors_attributes.values.each do |author_attributes|
    author_to_update = Author.find_by_id(author_attributes[:id]) || Author.find_by_username(author_attributes[:username]) || self.authors.build
    author_to_update.update_attributes(author_attributes)
  end
end

私はそのコードをテストしていませんが、うまくいくはずです。

編集: の他の機能を保持するにはaccepts_nested_Attributes_for、次を使用できますsuper

def authors_attributes=(authors_attributes)
  authors_attributes.each do |key, author_attributes|
    authors_attributes[key][:id] = Author.find_by_username(author_attributes[:username]).id if author_attributes[:username] && !author_attributes[:username].present?
  end
  super(authors_attributes)
end 

での実装がsuper機能しない場合、おそらく 2 つのオプションがあります。コントローラーで属性ハッシュの「処理」を続行する (ただし、コントローラーのプライベート メソッドに変更して、少しクリーンアップする) か、続行します。失った機能:destroy => trueを独自のコードで追加することによる私の最初のソリューションreject_ifです(これはそれほど難しくありません)。私はおそらく最初のオプションを使用します。

于 2012-06-29T07:25:58.560 に答える