更新表单,更新特定实例的所有记录

时间:2018-03-07 14:31:21

标签: ruby-on-rails ruby database activerecord

所以我正在研究食谱书栏应用程序。用户可以创建配方,查看配方并更新配方。然而,当我更新特定成分的数量(即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%>

3 个答案:

答案 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动态创建记录。