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]。数量?非常感谢您的回答。我能使它适应我的申请。