1

I'm doing a Ruby on Rails API. I have two models plans and categories, which has a relation "many to many" so I use an intermediate table with a model called category_plans. What I want is to create a plan with many categories, and each one of them has two more attributes (kind, portion) which will be saved on the intermediate table. (I'm using Postgresql).

Category

class Category < ApplicationRecord
    has_many :category_plans
    has_many :plan, through: :category_plans
end

Plan

class Plan < ApplicationRecord
    has_many :category_plans
    has_many :category, through: :category_plans
end

CategoryPlan

class CategoryPlan < ApplicationRecord
    validates_presence_of :category, :plan

    enum kind: {
        Colacion: 'Colacion',
        Desayuno: 'Desayuno',
        Comida: 'Comida',
        Cena: 'Cena'
    }, _prefix: :kind

    belongs_to :category
    belongs_to :plan
end

These are the migrations I use:

Plan

class CreatePlans < ActiveRecord::Migration[5.2]
    def change
        create_table :plans do |t|
            t.string :name
            t.references :patient, foreign_key: true
            t.text :description
            t.datetime :deleted_at
            t.timestamps
        end
    end
end

Category

class CreateCategories < ActiveRecord::Migration[5.2]
    def change
        create_table :categories do |t|
            t.string :name
            t.timestamps
        end
    end
end

CategoryPlan

class CreateCategoryPlans < ActiveRecord::Migration[5.2]
    def up
        execute <<-SQL
            CREATE TYPE type_food AS ENUM ('Colacion', 'Desayuno', 'Comida', 
            'Cena');
        SQL

        create_table :category_plans do |t|
            t.belongs_to :category, :null => false, :index => true
            t.belongs_to :plan, :null => false, :index => true
            t.column :kind  , :type_food
            t.float :portion
        end
        add_index :category_plans, :kind
    end

    def down
        drop_column :kind, :type_food
        execute "DROP kind type_food;"
    end
end

And the controller:

class V1::PlansController < ApplicationController
    before_action :set_patient, only: [:show, :update, :destroy]

    def create
        plan = Plan.new(plan_params)
        if plan.save
            render json: {status: 'Success', message: 'Saved plan', data: 
            plan}, status: 201
        else
            render json: {status: 'Error', message: 'Plan not saved', data: 
            plan.errors}, status: :unprocessable_entity
        end
    end

    def plan_params
        params.require(:plan).permit(:name, :patient_id, :description, 
        category_plans_attributes: [:id, :kind, :portion, 
        category_attributes: [:id]])
    end

end

I'm not sure that "strong params" is right, also I'm not sure about how to do my JSON structure, at this time I got it like this:

JSON

{
    "plan": {
        "name": "Plan 8",   
        "patient_id": "3",
        "description": "Plan nuevo",
        "category_plan": {
            "kind": "Cena",
            "portion": "12.3"
        }
    } 
}

1 Answers1

0

Your submitted JSON needs to be the same as the permitted attributes in plan_params

{
    "plan": {
        "name": "Plan 8",   
        "patient_id": "3",
        "description": "Plan nuevo",
        "category_plans_attributes": [{
            "kind": "Cena",
            "portion": "12.3"
        }]
    } 
}

Also you need to add the accepts_nested_attributes_for : category_plans to the Plan model.

Try that and check the rails app logs for errors if it does not succeed after this.

Simon L. Brazell
  • 1,132
  • 8
  • 14