所以我正在研究食谱书栏应用程序。用户可以创建配方,查看配方并更新配方。然而,当我更新特定成分的数量(即Pasta" 2 Cups")时,它会将包含面食的所有其他食谱更改为新数量(" 2 Cups") 。我可以在我的rails控制台和服务器中看到它识别出更改和更新,但是当我显示视图时,它看起来好像它显示了第一个成分实例,而不是我刚更新的那个实例。我有一种强烈的感觉,这是我的成分中的数量方法的错误,但我不知道如何解决它。
食谱模型
class Recipe < ApplicationRecord
belongs_to :user, required: false
has_many :recipe_ingredients
has_many :ingredients, through: :recipe_ingredients
validates :name, presence: true
validates :instructions, presence: true
validates :cooktime, presence: true
def self.alphabetize
self.order(name: :asc)
end
def ingredients_attributes=(ingredients_attributes)
self.ingredients = []
ingredients_attributes.values.each do |ingredients_attribute|
if !ingredients_attribute[:name].empty?
new_ingredient = Ingredient.find_or_create_by(name:
ingredients_attribute[:name])
self.recipe_ingredients.build(ingredient_id: new_ingredient.id,
quantity: ingredients_attribute[:quantity])
end
end
end
end
成分模型
class Ingredient < ApplicationRecord
has_many :recipe_ingredients
has_many :recipes, through: :recipe_ingredients
def self.alphabetize
self.order(name: :asc)
end
def quantity
recipe_ingredient = RecipeIngredient.find_by(recipe_id:
self.recipes.first.id, ingredient_id: self.id)
recipe_ingredient.quantity
end
end
配方显示,编辑和更新操作:
def show
@recipe = Recipe.find(params[:id])
@ingredients = @recipe.ingredients.alphabetize
end
def edit
@recipe = Recipe.find(params[:id])
end
def update
@recipe = Recipe.find(params[:id])
if @recipe.user = current_user
if @recipe.update(recipe_params)
redirect_to @recipe
else
render :edit
end
end
end
views / recipes / show(成分 - 数量)列表:
<% @recipe.ingredients.each do |ingredient|%>
<li><%=ingredient.name %> - <%=ingredient.quantity%></li>
<%end%>
答案 0 :(得分:0)
看起来这是问题所在:
class Ingredient < ApplicationRecord
def quantity
recipe_ingredient = RecipeIngredient.find_by(recipe_id:
self.recipes.first.id, ingredient_id: self.id)
recipe_ingredient.quantity
end
无论食谱如何, <%=ingredient.quantity%>
都会返回使用该成分的第一个配方的数量。我认为您应该访问recipe_ingerdient.quantity
。
<% @recipe.recipe_ingredients.each do |recipe_ingredient|%>
<li><%=recipe_ingredient.name %> - <%=recipe_ingredient.quantity%></li>
<%end%>
因为看起来每个食谱的数量都保存在recipe_ingredient
联接表中。
修改:此方法需要将delegate :name, to: :ingredient
添加到class RecipeIngredient
答案 1 :(得分:0)
只是对abax的答案进行了一遍。我想我会做类似的事情:
class Ingredient < ApplicationRecord
has_many :recipe_ingredients
has_many :recipes, through: :recipe_ingredients
def self.alphabetize
self.order(name: :asc)
end
def quantity_for(recipe)
recipe_ingredients.where(recipe: recipe).first.quantity
end
end
您可以使用以下内容:
<% @recipe.ingredients.each do |ingredient|%>
<li>
<%= ingredient.name %> - <%= ingredient.quantity_for(@recipe) %>
</li>
<%end%>
IMO,ingredient.quantity_for(@recipe)
非常清楚地表明发生了什么:ingredient
正在返回quantity_for
特定@recipe
。
def quantity
recipe_ingredient = RecipeIngredient.find_by(
recipe_id: self.recipes.first.id,
ingredient_id: self.id
)
recipe_ingredient.quantity
end
各种各样的错误。除了abax识别的问题之外,您不需要执行
RecipeIngredient.find_by(ingredient_id: self.id)
这就是你做has_many recipe_ingredients
的原因!这样你就可以做到:
recipe_ingredients.where(recipe: recipe)
并且,请注意,当您进行关联时,您不必说recipe_id: recipe.id
。 Rails允许您简单地执行recipe: recipe
。减少打字。错别字。
最后,这个任务:
recipe_ingredient = RecipeIngredient.find_by(...)
没有必要。你可以这么做:
RecipeIngredient.find_by(
recipe: recipe,
ingredient: self
).quantity
当然,你永远不会这样做。因为,如上所述,它做得更好:
recipe_ingredients.where(recipe: recipe).first.quantity
答案 2 :(得分:0)
我会从一个更温和的目标开始 - 只是为了让用户创建嵌套的RecipeIngedients:
<%= form_for(@recipe) do |f| %>
<%= fields_for(:recipe_ingredients) do |ri| %>
<%= ri.collection_select(:ingredient_id, Ingredient.all, :id, :name) %>
<%= ri.text_field(:quantity) %>
<% end %>
<% end %>
class Recipe < ApplicationRecord
# ...
accepts_nested_attributes_for :recipe_ingredients
end
def recipe_attributes
params.require(:recipe).permit(:foo, :bar, recipe_ingredient_attributes: [:ingredient_id, :quantity])
end
然后,您可以使用其他级别的嵌套属性来扩展它:
<%= form_for(@recipe) do |f| %>
<%= f.fields_for(:recipe_ingredients) do |ri| %>
<div class="recipe-ingredient">
<%= ri.collection_select(:ingredient_id, Ingredient.all, :id, :name) %>
<%= ri.fields_for(:ingredient) |ingredient| %>
<%= ingredient.text_field :name %>
<% end %>
</div>
<% end %>
<% end %>
class RecipeIngredient < ApplicationRecord
# ...
accepts_nested_attributes_for :ingredient, reject_if: [:ingredient_exists?,
private
def ingredient_exists?(attributes)
Ingredient.exists?(name: attributes[:name])
end
def ingredient_set?(attributes)
self.ingredient.nil?
end
end
def recipe_attributes
params.require(:recipe)
.permit(:foo, :bar,
recipe_ingredients_attributes: [:ingredient_id, :quantity, { ingredient_attributes: [:name] }]
)
end
但是,使用多个级别的嵌套属性通常会变成一个真正的混乱,因为你将如此多的功能集成到一个控制器中。从编程和UX角度来看,更好的选择可能是使用ajax设置自动完成并通过POST到/ingredients
动态创建记录。