如何编写控制器以从 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对象,要么根本不应该尝试在recipe控制器中创建ingredient对象。

编辑

形式变化自

<%= 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)

我相信你只需要改变你的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

因为您是通过recipe参数传递ingredients_params,所以您无需单独创建成分 - 它们将由accepts_nested_attributes_for在您的食谱模式下自动创建。

有关嵌套表单的永恒教程,请参阅 Ryan Bates 的 Railscasts 上的嵌套模型表单。

最新更新