Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding nested forms with multiple models (many-to-many association) #461

Closed
MajQel opened this issue Oct 15, 2017 · 7 comments
Closed

Adding nested forms with multiple models (many-to-many association) #461

MajQel opened this issue Oct 15, 2017 · 7 comments

Comments

@MajQel
Copy link

MajQel commented Oct 15, 2017

Hello,

First of all cocoon is one of my top gems (I'm using it in every single application).

Recently I am struggling for quite some time with nested forms for has_many through association.

What I need?

Create association for 2 attributes of 2 models
Model 1. RecipeIngredient attribute quantity
Model 2. Ingredient attribute name

Controller Recipe

List of models:

 class Recipe < ApplicationRecord
    has_many :directions, inverse_of: :recipe
    has_many :recipe_ingredients, inverse_of: :recipe
    has_many :ingredients, through: :recipe_ingredients

    accepts_nested_attributes_for :ingredients,
                                    reject_if: proc { |attributes| attributes['name'].blank? },
                                    allow_destroy: true
    accepts_nested_attributes_for :directions,
                                    reject_if: proc { |attributes| attributes['step'].blank? },
                                    allow_destroy: true
    accepts_nested_attributes_for :recipe_ingredients,
                                    reject_if: proc { |attributes| attributes['quantity'].blank? },
                                    allow_destroy: true


    validates :tittle, :description, :image, presence: true
    has_attached_file :image, styles: { :medium => "400x400#" }
    validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/

end

class Ingredient < ApplicationRecord
    has_many :recipe_ingredient, inverse_of: :ingredient
    has_many :recipes, through: :recipe_ingredient
end

class RecipeIngredient < ApplicationRecord
  belongs_to :recipe
  belongs_to :ingredient, inverse_of: :recipe_ingredient

end

_form for recipe creation

.col-md-6
                %h3 Skladniki
                #ingredients
                    = f.simple_fields_for :recipe_ingredients do |recipe_ingredient|
                        =render 'recipe_ingredient_fields', f: recipe_ingredient
                    .links
                        = link_to_add_association 'Dodaj skladnik', f, :recipe_ingredients, class: "btn btn-default add-button", :wrap_object => Proc.new {|recipe_ingredient| recipe_ingredient.build_ingredient; recipe_ingredient }

Actual nesting:

.form-inline.clearfix
        .nested-fields
            = f.input :quantity, :label => "Ilość", input_html: { class: "form-input form-control" }
            = f.fields_for :ingredients_attributes do |ingr|
                = ingr.input :name, :label => "Nazwa", input_html: { class: "form-input form-control" }

            = link_to_remove_association "Usun", f, class: "form-button btn btn-default"

Recipe controller line responsible for accepting params (there is rather no need to place here other methods)

    def recipe_params
        params.require(:recipe).permit(:tittle, :description, :image, :portion, :preparation_time, ingredients_attributes: [:id, :name, :_destroy], directions_attributes: [:id, :step, :_destroy], recipe_ingredients_attributes: [:id, :quantity, :_destroy])
    end

Validation error (Ingredient name attribute seems to be not assiociated with recipe as it should be):

Recipe ingredients ingredient must exist

My research why this happens:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"nw14a3HkgSrjE+QpIK4POEzTOqBhtE/EMe+CarWkmI6CSRf0GAdWZGzJQRD4aPurNDNm96TJbt60mc1hl+1JPA==", "recipe"=>{"tittle"=>"Tosty", "description"=>"Testowy przepis", "preparation_time"=>"10.0", "portion"=>"2", "recipe_ingredients_attributes"=>{"1507996685991"=>{"quantity"=>"432", "ingredients_attributes"=>{"name"=>"fasdd"}, "_destroy"=>"false"}, "1507996689888"=>{"quantity"=>"2134432342", "ingredients_attributes"=>{"name"=>"dsad"}, "_destroy"=>"false"}}, "directions_attributes"=>{"0"=>{"step"=>"Krok1", "_destroy"=>"false", "id"=>"1"}, "1"=>{"step"=>"Krok2", "_destroy"=>"false", "id"=>"2"}}}, "commit"=>"Update Recipe", "id"=>"5"}
  Recipe Load (0.3ms)  SELECT  "recipes".* FROM "recipes" WHERE "recipes"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
Unpermitted parameter: ingredients_attributes
Unpermitted parameter: ingredients_attributes
   (0.2ms)  begin transaction
  Direction Load (0.3ms)  SELECT "directions".* FROM "directions" WHERE "directions"."recipe_id" = ? AND "directions"."id" IN (1, 2)  [["recipe_id", 5]]
   (0.2ms)  rollback transaction

Problem is this line:

    recipe_ingredients_attributes"=>{"1507996685991"=>{"quantity"=>"432", "ingredients_attributes"=>{"name"=>"fasdd"}, "_destroy"=>"false"},

So my actual question.... is there workaround to this?
Ingredient name needs to be passed to Recipe params instead of RecipeIngredient params.
Both attributes needs to be created with proper association.

@nathanvda
Copy link
Owner

The error in the logfile states that the ingredients_attributes parameter is not allowed. If you check the passed parameters you can see that the recipe_ingredients_attributes contains the ingredients_attributes and this is not allowed by your strong parameters definition (the recipe_params method): it has to support the correct nesting as well.

@MajQel
Copy link
Author

MajQel commented Oct 15, 2017

Hey Nathan,
Thank you for response.

As you can see in my post I actually did some debugging and know where is the problem.
The point is that I have no idea if there is any workaround for this.
Strong parameters must stay as they are.
I am sitting on this issue for quite some time (about two weeks or more) and still dont know how to fix that.
Is it even possible?

@nathanvda
Copy link
Owner

What do you mean, strong parameters have to stay as they are? You could just do something like:

def recipe_params
        params.require(:recipe).permit(:tittle, :description, :image, :portion, :preparation_time, 
          ingredients_attributes: [:id, :name, :_destroy], 
          directions_attributes: [:id, :step, :_destroy], 
          recipe_ingredients_attributes: [:id, :quantity, :_destroy, 
                 ingredients_attributes: [:id, :name, :_destroy]])
    end

@nathanvda
Copy link
Owner

@MajQel
Copy link
Author

MajQel commented Oct 15, 2017

I did as you said and got error

https://github.com/MajQel/Elena

unknown attribute 'ingredients_attributes' for RecipeIngredient.


`def recipe_params
		
params.require(:recipe).permit(:tittle, :description, :image, :portion, :preparation_time, directions_attributes: [:id, :step, :_destroy], ingredients_attributes: [:id, :name, :_destroy], recipe_ingredients_attributes: [:id, :quantity, :_destroy, ingredients_attributes: [:id, :name, :_destroy]])

	end`
.form-inline.clearfix

	.nested-fields
		= f.input :quantity, :label => "Ilość", input_html: { class: "form-input form-control" }
		= f.fields_for :ingredients_attributes do |ingr|
			= ingr.input :name, :label => "Nazwa", input_html: { class: "form-input form-control" }

		= link_to_remove_association "Usun", f, class: "form-button btn btn-default"`

@nathanvda
Copy link
Owner

I am sorry I missed this but it seems so obvious: the accepts_nested_attributes_for :ingredients is missing from the RecipeIngredient class. (this has to match how your form is build, how your form is build is how the params are build).

Please checkout the example project in detail.

@MajQel
Copy link
Author

MajQel commented Oct 15, 2017

Ah yes.... indeed that was the issue 🤦‍♂️

Also had switch
ingredients_attributes: [:id, :name, :_destroy]
to
ingredient_attributes: [:id, :name, :_destroy]

And now everything is working.

Thank you very much for your time Nathan 🥇
You saved my day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants