如何编写控制器来从Rails中的一个表单创建资源和嵌套资源?

时间:2016-12-12 18:21:11

标签: ruby-on-rails

我正在尝试在Rails 5中从一个表单创建一个recipe对象和多个嵌套的ingredient对象。就目前而言,我能够创建recipe个对象,但是给出了传递params以创建ingredient对象时出错,特别是NoMethodError

undefined method `ingredient' for #<Ingredient id: nil, ing: "s", amount: "s", recipe_id: nil>

这是我的表格:

<%= form_for(@recipe, url: recipes_new_path) do |f| %>
<h1>
    <%= f.label :name %><br>
    <%= f.text_field :name %><br>
</h1>
<h1>
    <%= f.label :steps %><br>
    <%= f.text_area :steps %><br>
</h1>
<%= f.fields_for :ingredient do |p| %>
    <h1>
        <%= p.label :ing %><br>
        <%= p.text_area :ing %><br>
    </h1>
    <h1>
        <%= p.label :amount %><br>
        <%= p.text_area :amount %><br>
    </h1>
<% end %>
<%= f.submit "Add" %><br>

包含在routes.rb中:

....
resources :recipes do
    resources :ingredients
end

包含在模特中:

class Ingredient < ApplicationRecord
  belongs_to :recipe
  ....
end

class Recipe < ApplicationRecord
    has_many :ingredients, dependent: :destroy
    ....
    accepts_nested_attributes_for :ingredients, allow_destroy: true
    validates_associated :ingredients
end

recipe的控制器:

....
def new
    @recipe = Recipe.new
    @ingredient = @recipe.ingredients.build
end

def create
    @recipe = Recipe.new(recipe_params)
    @recipe.save
    @ingredient = @recipe.ingredients.build(recipe_params[:ingredients_attributes])
    if @ingredient.save
        redirect_to recipe_path(@recipe)
    else
        render 'recipes/new'
    end
end
....
def recipe_params
    params.require(:recipe).permit(:name, :steps, ingredients_attributes: [:ing, :amount])
end

据我所知,我要么错误地传递params来创建ingredient对象,要么我不应该尝试在{{ingredient中创建一个recipe对象1}}控制器。

修改

更改表格
<%= f.fields_for :ingredient do |p| %>

<%= f.fields_for :ingredients do |p| %>

修改

完整错误:

Started POST "/recipes" for ::1 at 2016-12-14 09:19:43 -0600
Processing by RecipesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"RfBXiL7vFla2/SvLqR2WrgzsfFMOqVRpjTavmvrPMJ/YBhF7UR+CeGV5NNRNyQfuALuJXc9hafPre+wPOo6n2A==", 
"recipe"=>{"name"=>"a", "steps"=>"a", "ingredients_attributes"=>{"0"=>{"ing"=>"a", "amount"=>"a"}}}, 
"commit"=>"Add"}
    (0.1ms)  begin transaction
    (0.1ms)  rollback transaction
Completed 500 Internal Server Error in 37ms (ActiveRecord: 0.2ms)
NoMethodError (undefined method `ingredient' for #<Ingredient id: nil, ing: "a", amount: "a", recipe_id: nil>
Did you mean?  increment):  
app/controllers/recipes_controller.rb:14:in `create'
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (5.6ms)
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.8ms)
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (2.5ms)
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (154.0ms)
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/index.html.erb
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/_markup.html.erb (0.7ms)
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/console.js.erb within layouts/javascript
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/_inner_console_markup.html.erb within layouts/inlined_string
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/_inner_console_markup.html.erb within layouts/inlined_string (1.5ms)
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/_prompt_box_markup.html.erb within layouts/inlined_string
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/_prompt_box_markup.html.erb within layouts/inlined_string (0.9ms)
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/style.css.erb within layouts/inlined_string
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/style.css.erb within layouts/inlined_string (0.6ms)
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/console.js.erb within layouts/javascript (148.7ms)
Rendering /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/main.js.erb within layouts/javascript
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/main.js.erb within layouts/javascript (0.6ms)
Rendered /Users/JCamp1991/.rvm/gems/ruby-2.3.1/gems/web-console-3.1.1/lib/web_console/templates/index.html.erb (284.4ms)

1 个答案:

答案 0 :(得分:0)

我相信你只需要改变你的fields_for使用“成分”复数:

<%= f.fields_for :ingredient do |p| %>

这是因为你的食谱has_many :ingredients。如果您的食谱只有has_one :ingredient,那么您的表单将按书面形式运行。

更新:您还应更改create方法,如下所示:

def create
  @recipe = Recipe.new(recipe_params)
  if @recipe.save
      redirect_to recipe_path(@recipe)
  else
      render 'recipes/new'
  end
end

因为您将ingredients_params传递给recipe参数,所以您无需单独创建这些成分 - 它们将在配方模式下由accepts_nested_attributes_for自动创建。

有关嵌套表单的永恒教程,请参阅Railscast上的Ryan Bates'Nested Model Form