问题
Question:
I'm working with cocoon gemfile & following the ERB style of it without haml/slim. I have my github here - The branch I'm working on is "cmodel" - Link.
The error is: Unpermitted parameter: ingredient_attributes
Really what I don't understand is just about everything about the models. I get pluralization is a thing. I get that strong parameters is a thing. My understanding to implement them is a bit fuzzy. I've found the rails guides & edge rails guides too simple for the expanded use here. I've flailed around with 80+ changes and feel like I've borked my install completely.
Some extras that I did:
- adding excessive accepts_attributes_for to blindly in models trying to guess issue
I added a non-pluralized strong parameter to try and find out if this is the issue
added ingredients to the quantities_attributes - blindly hoping this was it
Removed all ingredient(s)_attribute(s)
I have a weird schema now, put stuff into db probably shouldn't have
Pulled the ingredients & quantities from permitted strong parameter list. Then added in quantities_attributes - with no issues. Then tried the same with ingredient(s)_attributes ... the issue still exists.
updated @ 08:29pm - 12/25/15:
Weird side effect ... 15 or 20 tests ago I noticed the 2nd field when you click the add ingredient link worked to add, but the 2nd column does not display when you click edit. Possibly related?
I guess in summary this should all work - it matches another example used by someone else & there's no reason it should fail according to an example.
The code is completely non-responsive to the pluralization in the strong parameters area - it's frustrating. I think my developing method is pretty borked at this point, going to take a break after 4 hours of this junk.
My console output for the current error: updated @ 08:29pm - 12/25/15
Started PATCH "/recipes/1" for 68.54.21.200 at 2015-12-26 03:00:14 +0000
Cannot render console from 68.54.21.200! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by RecipesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"OzJmuRuIMWVbJ1CpnCTyU52GhTEmrsOfiBiHACzIviN02MO48HBaZlX8JKvZvBXErqBfbXULnhcjT3gk2Zrh+A==", "recipe"=>{"title"=>"Recipe name", "description"=>"Recipe Description of Lindy's ...", "instruction"=>"Recipe Instruction", "quantities_attributes"=>{"0"=>{"amount"=>"8", "_destroy"=>"false", "id"=>"63"}, "1"=>{"amount"=>"9", "_destroy"=>"false", "id"=>"64"}, "1451098805028"=>{"amount"=>"11", "ingredient_attributes"=>{"name"=>"11"}, "_destroy"=>"false"}}}, "commit"=>"Update Recipe", "id"=>"1"}
Recipe Load (0.2ms) SELECT "recipes".* FROM "recipes" WHERE "recipes"."id" = ? LIMIT 1 [["id", 1]]
Unpermitted parameter: ingredient_attributes
(0.1ms) begin transaction
Quantity Load (0.3ms) SELECT "quantities".* FROM "quantities" WHERE "quantities"."recipe_id" = ? AND "quantities"."id" IN (63, 64) [["recipe_id", "1"]]
SQL (0.4ms) INSERT INTO "quantities" ("amount", "recipe_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["amount", 11], ["recipe_id", "1"], ["created_at", "2015-12-26 03:00:14.818224"], ["updated_at", "2015-12-26 03:00:14.818224"]]
(9.6ms) commit transaction
Redirected to https://cocoontest-mirv.c9users.io/recipes/1
Completed 302 Found in 19ms (ActiveRecord: 10.7ms)
Setup
- I ran the bundle install for the gemfile.
- I made the entry in assests/application.js for the "//= require cocoon". It's been compiled and raked.
- I setup a controller to view ingredient model directly and can add them manually through the ingredient page - but it never saves my ingredient via the Recipe model.
Models:
class Recipe < ActiveRecord::Base
has_many :quantities
has_many :ingredient,
:through => :quantities
accepts_nested_attributes_for :quantities,
:allow_destroy => true
accepts_nested_attributes_for :ingredient
end
class Quantity < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
accepts_nested_attributes_for :ingredient
end
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, through: :quantities
accepts_nested_attributes_for :quantities
end
Controller: updated @ 08:29pm - 12/25/15
class RecipesController < ApplicationController
before_action :set_recipe, only: [:show, :edit, :update, :destroy]
# GET /recipes
# GET /recipes.json
def index
@recipes = Recipe.all
end
# GET /recipes/1
# GET /recipes/1.json
def show
@recipe = Recipe.find(params[:id])
end
# GET /recipes/new
def new
@recipe = Recipe.new
end
# GET /recipes/1/edit
def edit
@recipe = Recipe.find(params[:id])
end
# POST /recipes
# POST /recipes.json
def create
@recipe = Recipe.new(recipe_params)
respond_to do |format|
if @recipe.save
format.html { redirect_to @recipe, notice: 'Recipe was successfully created.' }
format.json { render :show, status: :created, location: @recipe }
else
format.html { render :new }
format.json { render json: @recipe.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /recipes/1
# PATCH/PUT /recipes/1.json
def update
respond_to do |format|
if @recipe.update(recipe_params)
format.html { redirect_to @recipe, notice: 'Recipe was successfully updated.' }
format.json { render :show, status: :ok, location: @recipe }
else
format.html { render :edit }
format.json { render json: @recipe.errors, status: :unprocessable_entity }
end
end
end
# DELETE /recipes/1
# DELETE /recipes/1.json
def destroy
@recipe.destroy
respond_to do |format|
format.html { redirect_to recipes_url, notice: 'Recipe was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_recipe
@recipe = Recipe.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def recipe_params
#seems to work, but used wrong pluralization for quantity, should have been quantities_attributes
params.require(:recipe).permit(:title, :description, :instruction,
:quantities_attributes => [:id, :ingredient, :ingredient_id, :recipe_id, :amount, :_destroy],
:ingredient_attributes => [:id, :_destroy, :ingredient_id, :name],
:ingredients_attributes => [:id, :_destroy, :ingredient_id, :name]
)
end
end
Here's the views ... portion from the github I linked above ...
edit.html.erb
<h1>Editing Recipe</h1>
<%= render 'form' %>
<%= link_to 'Show', @recipe %> |
<%= link_to 'Back', recipes_path %>
_form.html.erb
<%= form_for @recipe, html: {class: "form-horizontal"} do |f| %>
<% if @recipe.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@recipe.errors.count, "error") %> prohibited this recipe from being saved:</h2>
<ul>
<% @recipe.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<fieldset id="recipe-meta">
<ol>
<li class="control-group">
<%= f.label :title, "Recipe Name", class: "control-label" %>
<div class="controls"><%= f.text_field :title %></div>
</li>
<li class="control-group">
<%= f.label :description, "A brief description of this recipe", class: "control-label" %>
<div class="controls"><%= f.text_area :description, rows: 5 %></div>
</li>
<li class="control-group">
<%= f.label :instruction, "Instructions for this recipe", class: "control-label" %>
<div class="controls"><%= f.text_area :instruction, rows: 10 %></div>
</li>
</ol>
</fieldset>
<fieldset id="recipe-ingredients">
<ol>
<%= f.fields_for :quantities do |quantity| %>
<%= render 'quantity_fields', f: quantity %>
<% end %>
</ol>
<%= link_to_add_association 'add ingredient', f, :quantities, 'data-association-insertion-node' => "#recipe-ingredients ol", 'data-association-insertion-method' => "append", :wrap_object => Proc.new {|quantity| quantity.build_ingredient; quantity } %>
</fieldset>
<%= f.submit %>
</div>
<% end %>
_quantity_fields.html.erb
<li class="control-group nested-fields">
<div class="controls">
<%= f.label :amount, "Amount:" %>
<%= f.text_field :amount %>
<%= f.fields_for :ingredient do |quantity_ingredient| %>
<%= quantity_ingredient.text_field :name %>
<% end %>
<%= link_to_remove_association "remove", f %>
</div>
</li>
Schema
ActiveRecord::Schema.define(version: 20151206204139) do
create_table "ingredients", force: :cascade do |t|
t.text "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "ingredient_id"
end
create_table "quantities", force: :cascade do |t|
t.integer "amount"
t.text "ingredient"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "recipe_id"
t.integer "ingredient_id"
end
add_index "quantities", ["recipe_id"], name: "index_quantities_on_recipe_id"
create_table "recipes", force: :cascade do |t|
t.string "title"
t.text "description"
t.text "instruction"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "recipe_id"
end
add_index "recipes", ["recipe_id"], name: "index_recipes_on_recipe_id"
end
Update: Including view source of edit page ... here's the html page resulting from project so far ...
<!DOCTYPE html>
<html>
<head>
<title>Workspace</title>
<link rel="stylesheet" media="all" href="/assets/ingredients.self-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css?body=1" data-turbolinks-track="true" />
<link rel="stylesheet" media="all" href="/assets/quantities.self-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css?body=1" data-turbolinks-track="true" />
<link rel="stylesheet" media="all" href="/assets/recipes.self-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css?body=1" data-turbolinks-track="true" />
<link rel="stylesheet" media="all" href="/assets/scaffolds.self-d2e5ccad1506299feea3a35bfb7c525e101bb3b214303715deb675fdc539948b.css?body=1" data-turbolinks-track="true" />
<link rel="stylesheet" media="all" href="/assets/application.self-e80e8f2318043e8af94dddc2adad5a4f09739a8ebb323b3ab31cd71d45fd9113.css?body=1" data-turbolinks-track="true" />
<script src="/assets/jquery.self-a714331225dda820228db323939889f149aec0127aeb06255646b616ba1ca419.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/jquery_ujs.self-d456baa54c1fa6be2ec3711f0a72ddf7a5b2f34a6b4f515f33767d6207b7d4b3.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/turbolinks.self-c37727e9bd6b2735da5c311aa83fead54ed0be6cc8bd9a65309e9c5abe2cbfff.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/ingredients.self-877aef30ae1b040ab8a3aba4e3e309a11d7f2612f44dde450b5c157aa5f95c05.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/quantities.self-877aef30ae1b040ab8a3aba4e3e309a11d7f2612f44dde450b5c157aa5f95c05.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/recipes.self-877aef30ae1b040ab8a3aba4e3e309a11d7f2612f44dde450b5c157aa5f95c05.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/cocoon.self-05633fd25797688a657c08b26b8d4217031ee589f5ca76f0a57c11ac7d0e76ec.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/application.self-7862a8a8b42407b4741a1adeeea35f0d13ddc4f702ec532adb0674491d296495.js?body=1" data-turbolinks-track="true"></script>
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="Yco8LpQtKNyieVhjN1HKDzyMMyMfVHJ+UpllUl7iR88uIJkvf9VD36yiLGFyyS2YD6rpf0zxL/b5zpp2q7AYFA==" />
</head>
<body>
<h1>Editing Recipe</h1>
<form class="form-horizontal" id="edit_recipe_2" action="/recipes/2" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="_method" value="patch" /><input type="hidden" name="authenticity_token" value="DxvtQX6V7LtVoez4faEXT5L/t2vOORoprujYH4IXqJFA8UhAlW2HuFt6mPo4OfDYodltN52cR6EFvyc7d0X3Sg==" />
<fieldset id="recipe-meta">
<ol>
<li class="control-group">
<label class="control-label" for="recipe_title">Recipe Name</label>
<div class="controls"><input type="text" value="Test2" name="recipe[title]" id="recipe_title" /></div>
</li>
<li class="control-group">
<label class="control-label" for="recipe_description">A brief description of this recipe</label>
<div class="controls"><textarea rows="5" name="recipe[description]" id="recipe_description">
Test2</textarea></div>
</li>
<li class="control-group">
<label class="control-label" for="recipe_instruction">Instructions for this recipe</label>
<div class="controls"><textarea rows="10" name="recipe[instruction]" id="recipe_instruction">
Test2</textarea></div>
</li>
</ol>
</fieldset>
<fieldset id="recipe-ingredients">
<ol>
<li class="control-group nested-fields">
<div class="controls">
<label for="recipe_quantities_attributes_0_amount">Amount:</label>
<input type="text" value="111" name="recipe[quantities_attributes][0][amount]" id="recipe_quantities_attributes_0_amount" />
<input type="hidden" name="recipe[quantities_attributes][0][_destroy]" id="recipe_quantities_attributes_0__destroy" value="false" /><a class="remove_fields existing" href="#">remove</a>
</div>
</li>
<input type="hidden" value="38" name="recipe[quantities_attributes][0][id]" id="recipe_quantities_attributes_0_id" />
<li class="control-group nested-fields">
<div class="controls">
<label for="recipe_quantities_attributes_1_amount">Amount:</label>
<input type="text" value="111" name="recipe[quantities_attributes][1][amount]" id="recipe_quantities_attributes_1_amount" />
<input type="hidden" name="recipe[quantities_attributes][1][_destroy]" id="recipe_quantities_attributes_1__destroy" value="false" /><a class="remove_fields existing" href="#">remove</a>
</div>
</li>
<input type="hidden" value="39" name="recipe[quantities_attributes][1][id]" id="recipe_quantities_attributes_1_id" />
<li class="control-group nested-fields">
<div class="controls">
<label for="recipe_quantities_attributes_2_amount">Amount:</label>
<input type="text" value="1111" name="recipe[quantities_attributes][2][amount]" id="recipe_quantities_attributes_2_amount" />
<input type="hidden" name="recipe[quantities_attributes][2][_destroy]" id="recipe_quantities_attributes_2__destroy" value="false" /><a class="remove_fields existing" href="#">remove</a>
</div>
</li>
<input type="hidden" value="40" name="recipe[quantities_attributes][2][id]" id="recipe_quantities_attributes_2_id" />
<li class="control-group nested-fields">
<div class="controls">
<label for="recipe_quantities_attributes_3_amount">Amount:</label>
<input type="text" value="2222" name="recipe[quantities_attributes][3][amount]" id="recipe_quantities_attributes_3_amount" />
<input type="hidden" name="recipe[quantities_attributes][3][_destroy]" id="recipe_quantities_attributes_3__destroy" value="false" /><a class="remove_fields existing" href="#">remove</a>
</div>
</li>
<input type="hidden" value="41" name="recipe[quantities_attributes][3][id]" id="recipe_quantities_attributes_3_id" />
<li class="control-group nested-fields">
<div class="controls">
<label for="recipe_quantities_attributes_4_amount">Amount:</label>
<input type="text" value="33" name="recipe[quantities_attributes][4][amount]" id="recipe_quantities_attributes_4_amount" />
<input type="hidden" name="recipe[quantities_attributes][4][_destroy]" id="recipe_quantities_attributes_4__destroy" value="false" /><a class="remove_fields existing" href="#">remove</a>
</div>
</li>
<input type="hidden" value="42" name="recipe[quantities_attributes][4][id]" id="recipe_quantities_attributes_4_id" />
<li class="control-group nested-fields">
<div class="controls">
<label for="recipe_quantities_attributes_5_amount">Amount:</label>
<input type="text" value="444" name="recipe[quantities_attributes][5][amount]" id="recipe_quantities_attributes_5_amount" />
<input type="hidden" name="recipe[quantities_attributes][5][_destroy]" id="recipe_quantities_attributes_5__destroy" value="false" /><a class="remove_fields existing" href="#">remove</a>
</div>
</li>
<input type="hidden" value="43" name="recipe[quantities_attributes][5][id]" id="recipe_quantities_attributes_5_id" /> </ol>
<a data-association-insertion-node="#recipe-ingredients ol" data-association-insertion-method="append" class="add_fields" data-association="quantity" data-associations="quantities" data-association-insertion-template="<li class="control-group nested-fields">
<div class="controls">
<label for="recipe_quantities_attributes_new_quantities_amount">Amount:</label>
<input type="text" name="recipe[quantities_attributes][new_quantities][amount]" id="recipe_quantities_attributes_new_quantities_amount" />
<input type="text" name="recipe[quantities_attributes][new_quantities][ingredient_attributes][name]" id="recipe_quantities_attributes_new_quantities_ingredient_attributes_name" />
<input type="hidden" name="recipe[quantities_attributes][new_quantities][_destroy]" id="recipe_quantities_attributes_new_quantities__destroy" value="false" /><a class="remove_fields dynamic" href="#">remove</a>
</div>
</li>" href="#">add ingredient</a>
</fieldset>
<input type="submit" name="commit" value="Update Recipe" />
</div>
</form>
<a href="/recipes/2">Show</a> |
<a href="/recipes">Back</a>
</body>
</html>
回答1:
Your recipe_params
method needs to be changed - you actually have two levels of nesting in your form (Recipe -> Quantity -> Ingredient) so your parameter permitting needs to reflect this.
In your method, remove the ingredient_attributes
and ingredients_attributes
part - they don't match the format of your submitted params. Instead, you want something like the following:
:quantities_attributes => [:id, :ingredient, :ingredient_id, :recipe_id, :amount,
:_destroy, :ingredient_attributes => [:id, :_destroy, :ingredient_id, :name]]
来源:https://stackoverflow.com/questions/34457789/ruby-on-rails-4-missing-form-field-after-commit-error-is-unpermitted-paramet