Rails在多对多上嵌套表单:如何防止重复?

时间:2012-05-23 10:11:39

标签: ruby ruby-on-rails-3 many-to-many nested-forms nested-attributes

我在我的rails 3.2.3应用程序中设置了一个嵌套表单,它运行正常,我的模型是:

class Recipe < ActiveRecord::Base
  attr_accessible :title, :description, :excerpt, :date, :ingredient_lines_attributes

  has_and_belongs_to_many :ingredient_lines
  accepts_nested_attributes_for :ingredient_lines
end

class IngredientLine < ActiveRecord::Base
  attr_accessible :ingredient_id, :measurement_unit_id, :quantity

  has_and_belongs_to_many :recipes
  belongs_to :measurement_unit
  belongs_to :ingredient
end

如上所述,配方可以有多个IngredientLines,反之亦然。

我想避免的是在IngredienLine表上记录重复。

例如,想象一下,对于recipe_1,一个有{{measurement_unit_id“=>的IngredientLine; 1,“ingredient_id”=&gt; 1,“数量”=&gt; 3.5}是关联的,如果对于recipe_5,用户使用相同的值编译IngredientLine子表单,我不希望在IngredientLine表上有新记录,而只需要连接表中的新关联记录ingredient_lines_recipes。

请注意,目前我没有任何IngredientLine控制器,因为保存和更新IngredientLines由嵌套表单例程处理。甚至我的食谱控制器也很简单和标准:

class RecipesController < ApplicationController
  respond_to :html

  def new
    @recipe = Recipe.new
  end

  def create
    @recipe = Recipe.new(params[:recipe])
    flash[:notice] = 'Recipe saved.' if @recipe.save  
    respond_with(@recipe)
  end

  def destroy
    @recipe = Recipe.find(params[:id])
    @recipe.destroy
    respond_with(:recipes)
  end

  def edit
    respond_with(@recipe = Recipe.find(params[:id]))
  end

  def update
    @recipe = Recipe.find(params[:id])
    flash[:notice] = 'Recipe updated.' if @recipe.update_attributes(params[:recipe])
    respond_with(@recipe)
  end

end

我的猜测应该足以覆盖使用create的IngredientLine的标准find_or_create行为,但我不知道如何实现它。

但还有另外一个需要注意的重点,想象一下编辑一个包含一些IngredientLines的子表单,如果我添加另一个已存储在IngredientLine表中的IngredientLine,当然不应该在IngredientLine表上写任何东西,但是还应该区分已经与父项关联的子记录和需要创建关系的新子记录,在联接表上写一条新记录。

谢谢!

3 个答案:

答案 0 :(得分:1)

在配方模型重新定义方法

def ingredient_lines_attributes=(attributes)
   self.ingredient_lines << IngredientLine.where(attributes).first_or_initialize
end

答案 1 :(得分:1)

我遇到了类似的情况,并在this answer找到灵感。简而言之,我不担心嵌套模型的重复,直到节省时间。

翻译为您的示例,我将autosave_associated_records_for_ingredient_lines添加到Recipe。它贯穿ingredient_lines并执行find_or_create,正如你的直觉所说。如果ingredient_lines很复杂,Yuri的first_or_initialize方法可能更清晰。

我相信这有你正在寻找的行为:嵌套模型永远不会重复,但编辑一个会导致新记录而不是更新共享记录。孤立ingredient_lines的可能性很大,但如果这是一个严重问题,您可以选择更新,如果该模型只有一个recipe的ID与当前ID匹配。

答案 2 :(得分:0)

老问题,但我遇到了同样的问题。忘了添加:id到带有rails 4 strong_parameters的白名单。

例如:

widgets_controller.rb

def widget_params
  params.require(:widget).permit(:name, :foos_attributes => [:id, :name, :_destroy],)
end

widget.rb

class Widget < ActiveRecord::Base
  has_many :foos, dependent: :destroy
  accepts_nested_attributes_for :foos, allow_destroy: true
end

foo.rb

class Foo < ActiveRecord::Base
  belongs_to :widget
end