Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 如何引用嵌套Rails表单中的现有模型实例?_Ruby On Rails_Ruby_Ruby On Rails 3_Forms - Fatal编程技术网

Ruby on rails 如何引用嵌套Rails表单中的现有模型实例?

Ruby on rails 如何引用嵌套Rails表单中的现有模型实例?,ruby-on-rails,ruby,ruby-on-rails-3,forms,Ruby On Rails,Ruby,Ruby On Rails 3,Forms,我正在尝试构建一个配方管理员应用程序,其中包含三个主要模型: 配方-特定菜肴的配方 成分-成分列表,经唯一性验证 数量-成分和配方之间的联接表,也反映了特定配方所需的特定成分的数量。 我正在使用一个嵌套表单(见下文),我在嵌套表单(,)上使用一个很棒的Railscast构建了该表单,以获得灵感。(由于此特定模式的需要,我的表单在某些方面比教程更复杂,但我能够以类似的方式使其工作。) 但是,在提交我的表格时,将重新创建列出的任何和所有成分,如果该成分已存在于DB中,则无法通过唯一性验证并阻止创建配

我正在尝试构建一个配方管理员应用程序,其中包含三个主要模型:

配方-特定菜肴的配方
成分-成分列表,经唯一性验证
数量-成分和配方之间的联接表,也反映了特定配方所需的特定成分的数量。

我正在使用一个嵌套表单(见下文),我在嵌套表单(,)上使用一个很棒的Railscast构建了该表单,以获得灵感。(由于此特定模式的需要,我的表单在某些方面比教程更复杂,但我能够以类似的方式使其工作。)

但是,在提交我的表格时,将重新创建列出的任何和所有成分,如果该成分已存在于DB中,则无法通过唯一性验证并阻止创建配方。总阻力

因此,我的问题是:是否有一种方法可以提交此表单,以便如果存在名称与我的一个配料名称字段匹配的配料,它将引用现有配料,而不是尝试创建具有相同名称的新配料

代码细节如下


Recipe.rb
中:

class Recipe < ActiveRecord::Base
  attr_accessible :name, :description, :directions, :quantities_attributes,
                  :ingredient_attributes

  has_many :quantities, dependent: :destroy
  has_many :ingredients, through: :quantities
  accepts_nested_attributes_for :quantities, allow_destroy: true
class Quantity < ActiveRecord::Base
  attr_accessible :recipe_id, :ingredient_id, :amount, :ingredient_attributes

  belongs_to :recipe
  belongs_to :ingredient
  accepts_nested_attributes_for :ingredient
class Ingredient < ActiveRecord::Base
  attr_accessible :name
  validates :name, :uniqueness => { :case_sensitive => false }

  has_many :quantities
  has_many :recipes, through: :quantities
class Ingredient < ActiveRecord::Base
  before_save { self.name.downcase! } # to simplify search and unified view
  validates :name, :uniqueness => { :case_sensitive => false }

  has_many :quantities
  has_many :recipes, through: :quantities
end
def new
  @recipe = Recipe.new
  3.times do
    @recipe.quantities.build #initialize recipe -> quantities association
    @recipe.quantities.last.build_ingredient #initialize quantities -> ingredient association
  end
end

def create
  @recipe = Recipe.new(recipe_params)    
  prepare_recipe

  if @recipe.save ... #now all saved in proper way
end

def update
  @recipe = Recipe.find(params[:id])
  @recipe.attributes = recipe_params
  prepare_recipe    

  if @recipe.save ... #now all saved in proper way
end

private 
def prepare_recipe
  @recipe.quantities.each do |quantity|
    # do case-insensitive search via 'where' and building SQL-request
    if ingredient = Ingredient.where('LOWER(name) = ?', quantity.ingredient.name.downcase).first
      quantity.ingredient_id = quantity.ingredient.id = ingredient.id
    end
  end
end

def recipe_params
  params.require(:recipe).permit(
    :name,
    :description,
    :directions,
    :quantities_attributes => [
      :id,
      :amount,
      :_destroy,
      :ingredient_attributes => [
        #:id commented bc we pick 'id' for existing ingredients manually and for new we create it
        :name
  ]])
end
配料.rb
中:

class Recipe < ActiveRecord::Base
  attr_accessible :name, :description, :directions, :quantities_attributes,
                  :ingredient_attributes

  has_many :quantities, dependent: :destroy
  has_many :ingredients, through: :quantities
  accepts_nested_attributes_for :quantities, allow_destroy: true
class Quantity < ActiveRecord::Base
  attr_accessible :recipe_id, :ingredient_id, :amount, :ingredient_attributes

  belongs_to :recipe
  belongs_to :ingredient
  accepts_nested_attributes_for :ingredient
class Ingredient < ActiveRecord::Base
  attr_accessible :name
  validates :name, :uniqueness => { :case_sensitive => false }

  has_many :quantities
  has_many :recipes, through: :quantities
class Ingredient < ActiveRecord::Base
  before_save { self.name.downcase! } # to simplify search and unified view
  validates :name, :uniqueness => { :case_sensitive => false }

  has_many :quantities
  has_many :recipes, through: :quantities
end
def new
  @recipe = Recipe.new
  3.times do
    @recipe.quantities.build #initialize recipe -> quantities association
    @recipe.quantities.last.build_ingredient #initialize quantities -> ingredient association
  end
end

def create
  @recipe = Recipe.new(recipe_params)    
  prepare_recipe

  if @recipe.save ... #now all saved in proper way
end

def update
  @recipe = Recipe.find(params[:id])
  @recipe.attributes = recipe_params
  prepare_recipe    

  if @recipe.save ... #now all saved in proper way
end

private 
def prepare_recipe
  @recipe.quantities.each do |quantity|
    # do case-insensitive search via 'where' and building SQL-request
    if ingredient = Ingredient.where('LOWER(name) = ?', quantity.ingredient.name.downcase).first
      quantity.ingredient_id = quantity.ingredient.id = ingredient.id
    end
  end
end

def recipe_params
  params.require(:recipe).permit(
    :name,
    :description,
    :directions,
    :quantities_attributes => [
      :id,
      :amount,
      :_destroy,
      :ingredient_attributes => [
        #:id commented bc we pick 'id' for existing ingredients manually and for new we create it
        :name
  ]])
end
link_to
link_to_功能
允许动态添加和删除数量/成分对,并根据前面提到的Railscast进行了修改。他们可以使用一些重构,但或多或少都应该这样做


更新:根据Leger的请求,以下是来自
recipes\u controller.rb
的相关代码。在
Recipes#new
路线中,
3.times{@recipe.quantities.build}
为任何给定配方设置三个空白数量/成分对;可以使用上面提到的“添加成分”和“删除”链接动态删除或添加这些内容

class RecipesController < ApplicationController

  def new
    @recipe = Recipe.new
    3.times { @recipe.quantities.build }
    @quantity = Quantity.new
  end

  def create
    @recipe = Recipe.new(params[:recipe])

    if @recipe.save
      redirect_to @recipe
    else
      render :action => 'new'
    end
  end
class RecipesController“新建”
结束
结束

你不应该把成分匹配的逻辑放在眼里-在将它们传递给模型之前,创建合适的对象是
配方#的职责。请分享控制器的相关代码

在开始编写代码之前,请注意以下几点:

  • 我用Rails4@ruby2.0,但尝试编写与Rails3兼容的代码
  • attr\u acessible
    在Rails 4中被弃用,因此改用强参数。如果你想升级你的应用程序,从一开始就使用强大的参数
  • 建议将
    成分
    制成低外壳,以便在外壳不敏感的情况下提供均匀的外观
  • 好了,我们开始:

    删除
    Recipe.rb
    Quantity.rb
    component.rb
    中的
    attr\u accessible
    字符串

    不区分大小写,小写
    component.rb

    class Recipe < ActiveRecord::Base
      attr_accessible :name, :description, :directions, :quantities_attributes,
                      :ingredient_attributes
    
      has_many :quantities, dependent: :destroy
      has_many :ingredients, through: :quantities
      accepts_nested_attributes_for :quantities, allow_destroy: true
    
    class Quantity < ActiveRecord::Base
      attr_accessible :recipe_id, :ingredient_id, :amount, :ingredient_attributes
    
      belongs_to :recipe
      belongs_to :ingredient
      accepts_nested_attributes_for :ingredient
    
    class Ingredient < ActiveRecord::Base
      attr_accessible :name
      validates :name, :uniqueness => { :case_sensitive => false }
    
      has_many :quantities
      has_many :recipes, through: :quantities
    
    class Ingredient < ActiveRecord::Base
      before_save { self.name.downcase! } # to simplify search and unified view
      validates :name, :uniqueness => { :case_sensitive => false }
    
      has_many :quantities
      has_many :recipes, through: :quantities
    end
    
    def new
      @recipe = Recipe.new
      3.times do
        @recipe.quantities.build #initialize recipe -> quantities association
        @recipe.quantities.last.build_ingredient #initialize quantities -> ingredient association
      end
    end
    
    def create
      @recipe = Recipe.new(recipe_params)    
      prepare_recipe
    
      if @recipe.save ... #now all saved in proper way
    end
    
    def update
      @recipe = Recipe.find(params[:id])
      @recipe.attributes = recipe_params
      prepare_recipe    
    
      if @recipe.save ... #now all saved in proper way
    end
    
    private 
    def prepare_recipe
      @recipe.quantities.each do |quantity|
        # do case-insensitive search via 'where' and building SQL-request
        if ingredient = Ingredient.where('LOWER(name) = ?', quantity.ingredient.name.downcase).first
          quantity.ingredient_id = quantity.ingredient.id = ingredient.id
        end
      end
    end
    
    def recipe_params
      params.require(:recipe).permit(
        :name,
        :description,
        :directions,
        :quantities_attributes => [
          :id,
          :amount,
          :_destroy,
          :ingredient_attributes => [
            #:id commented bc we pick 'id' for existing ingredients manually and for new we create it
            :name
      ]])
    end
    
    prepare_recipe
    中,我们执行以下操作:

  • 查找具有给定名称的配料ID
  • 将外键
    数量。成分id
    设置为id
  • quantity.component.id
    设置为id(想想如果不这样做会发生什么,并更改配方中的配料名称)

  • 享受吧

    感谢您的回复——我已经在我的原始帖子底部添加了相关的控制器代码。注意,关于视图中的逻辑,但我不确定哪一行是令人不快的。我想确保用户在创建配方时可以添加配料及其数量,我试图将代码限制为表单所需的内容。更新配方时如何填充配料字段?上面的代码片段似乎不起作用。(Rails 4.1.4)另外,如何查询数量?有没有办法将其映射到配料?i、 e.@recipe.component[1]。数量?非常感谢您的回答。我能使它适应我的申请。