Search code examples
ruby-on-railsruby-on-rails-5rails-api

Unpermitted Parameter for Nested Resource in JSON Payload


I am having difficulty sending a JSON payload to an endpoint that contains a nested resource that I would like created with associations in-tact and persisted... no matter what I do I am presented in the console with Unpermitted parameter: :ingredients.

models/meal.rb

class Meal < ApplicationRecord
  has_many :ingredients
  accepts_nested_attributes_for :ingredients
end

models/ingredient.rb

class Ingredient < ApplicationRecord
  belongs_to :meal
end

controller/meals_controller.rb

class MealsController < ApplicationController
  def create
    @meal = Meal.new(meal_params)
    puts meal_params.inspect
    # just want to see the parameters in the log
    # ...
  end

  private
    def meal_params
      params.require(:meal).permit(
        :name,
        ingredients_attributes: [:id, :text]
      )
    end
  end

db/migrate/xxx_create_inredients.rb

class CreateIngredients < ActiveRecord::Migration[5.1]
  def change
    create_table :ingredients do |t|
      t.belongs_to :meal, index: true
      t.string :text
      t.timestamps
    end
  end
end

request JSON payload

{
  "meal": {
    "name": "My Favorite Meal",
    "ingredients": [
      { "text": "First ingredient" },
      { "text": "Second ingredient" }
    ]
  }
}

I tried another approach from a SO article encountering a similar problem that seemed to recognize the ingredients parameter, but ultimately threw a 500: params.require(:meal).permit(:name, { ingredients: [:id, :text] })

When doing that I receive the following: ActiveRecord::AssociationTypeMismatch (Ingredient(#70235637684840) expected, got {"text"=>"First Ingredient"} which is an instance of ActiveSupport::HashWithIndifferentAccess(#70235636442880))


Any help in pointing out my flaw is much appreciated. And yes, I want the nested ingredients resource to be part of the payload going to this endpoint.


Solution

  • The only problem that you have here is that you have in your meal params ingredients_attributes

    def meal_params
      params.require(:meal).permit(
        :name,
        ingredients_attributes: [:id, :text]
      )
    end
    

    so in your payload you need to have it with that key too

    {
      "meal": {
        "name": "My Favorite Meal",
        "ingredients_attributes": [
          { "text": "First ingredient" },
          { "text": "Second ingredient" }
        ]
      }
    }
    

    they need to match.