0
votes

Research: [Rails 4 Nested Attributes with fields_for Don't Save to Database] [Rails 4 - nested attributes with Cocoon gem]1[Rails 4.1 Nested Attributes and Fields For Getting Unpermitted Parameters and Not Saving]2

My specific problem is:

I have two nested forms: Ingredients and Directions

The ingredients save but the directions do not save. It doesn't throw up an error, nor do the logs have a clue as to if there even is a problem.

First, let's get the most common problem out of the way: incorrectly named attributes parameters for strong parameters. Mine is correctly plural.

class RecipesController < ApplicationController
def recipe_params
        params.require(:recipe).permit(:title, :image, directions_attributes: [:id, :name, :_destroy], ingredients_attributes: [:id, :name, :_destroy])
    end

And my models are setup correctly as well

class Recipe < ActiveRecord::Base
    has_many :ingredients
    has_many :directions
    belongs_to :user
    accepts_nested_attributes_for :ingredients, reject_if: :all_blank, allow_destroy: true
    accepts_nested_attributes_for :directions, reject_if: :all_blank, allow_destroy: true
    validates :title, presence: true
    mount_uploader :image, ImageUploader
end

#_form.html.haml
= simple_form_for @recipe, html: { multipart: true } do |f|
    - if @recipe.errors.any?
        #errors
            %p
                = @recipe.error.count
                Prevented this recipe from saving
            %ul
                %li= msg
    .panel-body
        = f.input :title, label: "Name", input_html: { class: 'form-control' }
        = f.input :image, label: "Picture",input_html: { class: 'form-control' }
        .row
            .col-md-6
                %h3 Ingredients
                #ingredients
                    = f.simple_fields_for :ingredients do |ingredient|
                        = render 'ingredient_fields', f: ingredient
                    .links
                        = link_to_add_association 'Add Ingredient', f, :ingredients, class: "btn btn-default add-button"

            .col-md-6
                %h3 Directions
                #directions
                    = f.simple_fields_for :directions do |direction|
                        = render 'direction_fields', f: direction
                    .links
                        = link_to_add_association 'Add Step', f, :directions, class: "btn btn-default add-button"

    = f.button :submit, class: "btn btn-primary"

Here are both my partials for ingredients and directions

_ingredient_fields.html.haml
.form-inline.clearfix
    .nested-fields
        = f.input :name, input_html: { class: "form-input form-control"}
        = link_to_remove_association 'Remove', f, class: "form-button btn btn-default"

_direction_fields.html.haml
.form-inline.clearfix
    .nested-fields
        = f.input :name, input_html: { class: "form-input form-control"}
        = link_to_remove_association 'Remove', f, class: "form-button btn btn-default"

Ok here is the funny thing, my app doesn't throw any errors. The name, image and ingredients save but the directions do not.

Here is the log

Started POST "/recipes" for ::1 at 2016-02-12 19:00:07 -0800 Processing by RecipesController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"YEXiv10mHkfdLPRFGHFGNJX2szJQVXK7gezeakEFbe+57afx6Ih1UjRS6tJNftDLsMI5NS1W84pf2sRhQi0J8g==", "recipe"=>{"title"=>"Honey Apple Chicken", "image"=>#, @original_filename="Honey-Mustard-Chicken-and-Apples.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"recipe[image]\"; filename=\"Honey-Mustard-Chicken-and-Apples.jpg\"\r\nContent-Type: image/jpeg\r\n">, "ingredients_attributes"=>{"1455332308170"=>{"name"=>"Chicken Thighs", "_destroy"=>"false"}, "1455332308175"=>{"name"=>"Honey", "_destroy"=>"false"}}, "directions_attributes"=>{"1455332325877"=>{"step"=>"Brown Chicken with skin on.", "_destroy"=>"false"}, "1455332325880"=>{"step"=>"Add apples", "_destroy"=>"false"}}}, "commit"=>"Create Recipe"} User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]] Unpermitted parameter: step Unpermitted parameter: step (0.1ms) begin transaction Recipe Exists (0.2ms) SELECT 1 AS one FROM "recipes" WHERE "recipes"."image" = ? LIMIT 1 [["image", "1455332407-84040-0233/Honey-Mustard-Chicken-and-Apples.jpg"]] SQL (0.4ms) INSERT INTO "recipes" ("title", "image", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["title", "Honey Apple Chicken"], ["image", "1455332407-84040-0233/Honey-Mustard-Chicken-and-Apples.jpg"], ["user_id", 1], ["created_at", "2016-02-13 03:00:07.756946"], ["updated_at", "2016-02-13 03:00:07.756946"]] SQL (0.2ms) INSERT INTO "ingredients" ("name", "recipe_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Chicken Thighs"], ["recipe_id", 1], ["created_at", "2016-02-13 03:00:07.758440"], ["updated_at", "2016-02-13 03:00:07.758440"]] SQL (0.1ms) INSERT INTO "ingredients" ("name", "recipe_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Honey"], ["recipe_id", 1], ["created_at", "2016-02-13 03:00:07.759655"], ["updated_at", "2016-02-13 03:00:07.759655"]] (2.5ms) commit transaction Redirected to http://localhost:3000/recipes/1 Completed 302 Found in 1499ms (ActiveRecord: 3.5ms)

Started GET "/recipes/1" for ::1 at 2016-02-12 19:00:09 -0800 Processing by RecipesController#show as HTML Parameters: {"id"=>"1"} Recipe Load (0.3ms) SELECT "recipes".* FROM "recipes" WHERE "recipes"."id" = ? LIMIT 1 [["id", 1]] User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]] Ingredient Load (0.3ms) SELECT "ingredients".* FROM "ingredients" WHERE "ingredients"."recipe_id" = ? [["recipe_id", 1]] Direction Load (0.1ms) SELECT "directions".* FROM "directions" WHERE "directions"."recipe_id" = ? [["recipe_id", 1]] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]] Rendered recipes/show.html.haml within layouts/application (9.2ms) Completed 200 OK in 47ms (Views: 41.2ms | ActiveRecord: 1.0ms)

I don't understand Karl's answer to his own question: Rails 4.1 Nested Attributes and Fields For Getting Unpermitted Parameters and Not Saving If you can see anything, you are Macgyver!

1
Karl's answer is not related: he is manipulating the javascript/html himself. The answer by @elias-sanchez seems correct to me: you do not allow step in your strong parameters, and this most likely will cause the models to be rejected (and then it would fail without error). But hard to tell for sure without any model code for your recipes.nathanvda

1 Answers

1
votes

You can change:

params.require(:recipe).permit(:title, :image, directions_attributes: [:id, :name, :_destroy], ingredients_attributes: [:id, :name, :_destroy])

for

params.require(:recipe).permit(:title, :image, directions_attributes: [:id, :name, :step, :_destroy], ingredients_attributes: [:id, :name, :_destroy])

you are trying to send "step" when is not permitted, if you have a not_blank validation in your Direction model for the setp column, it will not be saved.