Rails 4, двойные вложенные модели и сильные параметры

Я новичок в Ruby on Rails и сразу начал с rails 4.

Я успешно вложил модель рецепта и ингредиента, чтобы их можно было добавить в одной и той же форме. Затем я хочу вложить количество в ингредиент, чтобы его также можно было добавить в той же форме. Кажется, все работает нормально до тех пор, пока количество ингредиентов не будет вставлено в базу данных, и из этого я полагаю, что что-то не так с сильными параметрами в recipes_controller. Но я опубликую полный код ниже. Я использую simple_form для форм.

Благодарен за любую помощь!

Вот мои модели:

class Recipe < ActiveRecord::Base
  has_many :comments, dependent: :destroy
  has_many :ingredients, dependent: :destroy
  accepts_nested_attributes_for :ingredients, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
  validates :title, presence: true
  validates :desc, presence: true
end


class Ingredient < ActiveRecord::Base
  belongs_to :recipe
  has_many :quantities, dependent: :destroy
  accepts_nested_attributes_for :quantities, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end

class Quantity < ActiveRecord::Base
  belongs_to :ingredient
end

Вот recipes_controller

class RecipesController < ApplicationController

  def new
    @recipe = Recipe.new
    3.times do 
      ingredient = @recipe.ingredients.build
      1.times {ingredient.quantities.build }
    end
  end

  def create
    @recipe = Recipe.new(params[:recipe].permit(:title, :desc, ingredients_attributes: [:id, :recipe_id, :name, :_destroy, quantities_attributes: [:id, :ingredient_id, :amount, :unit, :_destroy]]))

    if @recipe.save
      redirect_to @recipe
    else
      render "new"
    end
  end

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


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


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

    if @recipe.update(params[:recipe].permit(:title, :desc))
      redirect_to @recipe
    else
      render 'edit'
    end
  end


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

    redirect_to recipes_path
  end





  def index
    @recipes = Recipe.all
  end

  private
    def post_params
      params.require(:recipe).permit(:title, :desc, ingredients_attributes: [:id, :recipe_id, :name, :_destroy, quantities_attributes: [:id, :ingredient_id, :amount, :unit, :_destroy]])
    end
end

Затем я использую простую форму, чтобы создать форму для рецепта, ингредиента и количества с помощью частей.

_форма:

<%= simple_form_for @recipe do |f| %>
    <%= f.error_notification %>


    <%= f.input :title %>
    <%= f.input :desc %>



    <%= f.simple_fields_for :ingredients do |builder| %>

    <%= render "ingredient_fields", :f => builder %>


    <% end %>

    <p class="links">
    <%= link_to_add_association 'add ingredient', f, :ingredients %>
    <p class="links">
    <%= f.error :base%>
    <%= f.submit %>

<% end %>

Что рендерится из _ingredients_fields:

<div class="nested-fields">
    <%= f.input :name, label: "Ingredient" %>



    <%= f.simple_fields_for :quantities do |builder| %>

    <%= render "quantities_fields", :f => builder %>

    <% end %>

    <%= link_to_remove_association "remove", f %>
</div>

который отображается из _quantities_fields: [EDITED]

<div class="nested-fields">
    <%= f.input :amount %>
    <%= f.input :unit %>
</div>

Попытка добавить новые рецепты приводит к следующему оператору журнала:

Started POST "/recipes" for 127.0.0.1 at 2013-10-29 14:15:40 +0100
Processing by RecipesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"t6LKgDLwAxaU9xo2ipyCM+j1yfVF9WrI8AoGTX+gRkw=", "recipe"=>{"title"=>"Pancakes", "desc"=>"Tasty", "ingredients_attributes"=>{"0"=>{"name"=>"Milk", "quantities_attributes"=>{"0"=>{"amount"=>"1", "unit"=>"Cup"}}, "_destroy"=>"false"}}}, "commit"=>"Create Recipe"}
  [1m[35m (0.1ms)[0m  begin transaction
  [1m[36mSQL (3.5ms)[0m  [1mINSERT INTO "recipes" ("created_at", "desc", "title", "updated_at") VALUES (?, ?, ?, ?)[0m  [["created_at", Tue, 29 Oct 2013 13:15:40 UTC +00:00], ["desc", "Tasty"], ["title", "Pancakes"], ["updated_at", Tue, 29 Oct 2013 13:15:40 UTC +00:00]]
  [1m[35mSQL (0.4ms)[0m  INSERT INTO "ingredients" ("created_at", "name", "recipe_id", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Tue, 29 Oct 2013 13:15:40 UTC +00:00], ["name", "Milk"], ["recipe_id", 27], ["updated_at", Tue, 29 Oct 2013 13:15:40 UTC +00:00]]
  [1m[36m (7.8ms)[0m  [1mcommit transaction[0m
Redirected to http://www.projectcookbook.dev/recipes/27
Completed 302 Found in 22ms (ActiveRecord: 11.7ms)

person Ebato    schedule 29.10.2013    source источник
comment
Возможно, шаблон объекта формы является хорошим способом решения сложной проблемы вложенных атрибутов ActiveRecord.   -  person Dmitry Shvetsov    schedule 15.02.2017


Ответы (2)


Вы используете аналогичный рендеринг для партиалов _quantities и _ingredients, что неверно. В _quantities_field вам не нужно

<%= f.simple_fields_for :quantities do |builder| %>
<%= render "quantities_fields", :f => builder %>
<% end %>

И должен настроить

<%= f.input :name, label: "Quantity" %>

в _quantities_fields.

UPD

Я думаю, что проблема в пункте :reject_if модели Ingredient. Так должно быть

:reject_if => lambda { |a| a[:amount].blank? }

bc здесь вы указываете условия для количества, а не для ингредиента

О стилях кода: 1) В контроллере лучше использовать соответствующее имя частного метода для сильных параметров: recipe_params вместо post_params, а затем использовать его для создания нового рецепта @recipe = Recipe.new(recipe_params)

2) Текущие связи между Рецептом, Ингредиентом и Количеством приведут к дублированию Ингредиента в случае, если два Рецепта используют один и тот же. Причина в belongs_to, которые определяют единую ассоциацию. Попробуйте другой подход (ниже).

КСТАТИ. Недавно я ответил на аналогичный вопрос. Проверьте это: Как мне сослаться на существующий экземпляр модели во вложенной форме Rails?

person Leger    schedule 29.10.2013
comment
Извините, мой плохой, я неправильно написал в коде в своем вопросе, по ошибке скопировав тот же код из _ingredient_fields. Я отредактировал это сейчас и написал настоящий код, с которым я работал. Так что проблема была не в этом. - person Ebato; 29.10.2013
comment
Кроме того, я отметил, что в предыдущем вопросе, который вы рекомендовали, он использовал несколько иную структуру между моделями, соединяя рецепт и ингредиент через количество. Но я не думаю, что это то, что вызывает мою проблему, или это так? Благодарен за любую помощь! - person Ebato; 29.10.2013
comment
Да, проблема в модели ингредиентов, а не в форме - person Leger; 30.10.2013

Я думаю, что вам не хватает в этой части

<%= simple_form_for @recipe do |f| %>

так должно быть

<%= simple_nested_form_for @recipe do |f| %>

person dompuAmanat Fadilla    schedule 27.10.2014