- UPDATED - Rails API应用程序使用来自控制器的POST请求一次创建多个不同模型的嵌套记录



我正在开发一个API Ruby on rails 6.1应用程序,所以我所有的响应都是JSON格式的。我试图创建一个3级嵌套记录,这意味着我想创建一个计划记录,其中包含许多天的记录,其中包含每天的许多餐。

Plan.rb

class Plan < ApplicationRecord
    has_and_belongs_to_many :days
    has_and_belongs_to_many :meals
    has_one_attached :image, dependent: :destroy
    accepts_nested_attributes_for :meals, reject_if: ->(object) { object[:name].blank? }
    accepts_nested_attributes_for :days, reject_if: ->(object) { object[:number].blank? }
end

Day.rb

class Day < ApplicationRecord
     has_and_belongs_to_many :plans
     has_and_belongs_to_many :meals
     validates_presence_of :number, on: [:create, :update], message: "can't be blank"
     validates_uniqueness_of :number, on: [:create, :update], message: "You can't use same day number twice"
     accepts_nested_attributes_for :meals, reject_if: ->(object) { object[:name].blank? }
end

Meal.rb

class Meal < ApplicationRecord
  has_and_belongs_to_many :days
  has_and_belongs_to_many :plans
end

我还增加了2个join表

  create_table "days_meals", id: false, force: :cascade do |t|
     t.bigint "day_id", null: false
     t.bigint "meal_id", null: false
  end
  create_table "days_plans", id: false, force: :cascade do |t|
     t.bigint "day_id", null: false
     t.bigint "plan_id", null: false
  end

,这是更新后的plans_controller.rb

# POST /create_custon_plan
 def create_custon_plan
    @plan = Plan.new(plan_params)
    if @plan.save
      render json: {
        messages: "Plan was successfully created.",
        is_success: true,
        status: :created,
        data: { plan: @plan, days_attributes: @plan.days, meals_attributes: @plan.meals },
      }
    else
      render json: @plan.errors, status: :unprocessable_entity
    end
  end

这就是我允许参数

的方式
def plan_params
    params.require(:plan).permit(:name, :monthly_price, :image_url, days_attributes: [:number, meals_attributes: [:name, :calories, :protein, :fat, :carbohydrates, :categorie]])
end

This is my POST请求正文到http://localhost:3000/api/create_custon_plan

{
    "name": "Test Plan",
    "monthly_price": 0,
    "image_url": "55555",
    "days_attributes": [
                {
                "number": 500,
                "meals_attributes": [
                            {
                                "name": "azerazer Salad",
                                "calories": 55,
                                "protein": 55,
                                "fat": 55,
                                "carbohydrates": 55,
                                "image_url": "55555",
                                "categorie": "snack-1"
                            },
                            {
                                "name": "Fit Burger",
                                "calories": 55,
                                "protein": 55,
                                "fat": 55,
                                "carbohydrates": 55,
                                "image_url": "55555",
                                "categorie": "meal-1"
                            },
                            {
                                "name": "Vegan Rataouille",
                                "calories": 55,
                                "protein": 55,
                                "fat": 55,
                                "carbohydrates": 55,
                                "image_url": "55555",
                                "categorie": "snack-2"
                            },
                            {
                                "name": "Chicken BBQ",
                                "calories": 55,
                                "protein": 55,
                                "fat": 55,
                                "carbohydrates": 55,
                                "image_url": "55555",
                                "categorie": "meal-3"
                            }
                        ]
                },
                {
                "number": 502,
                "meals_attributes": 
                        [
                            {
                                "name": "Woldrof Salad",
                                "calories": 55,
                                "protein": 55,
                                "fat": 55,
                                "carbohydrates": 55,
                                "image_url": "55555",
                                "categorie": "snack-1"
                            },
                            {
                                "name": "Baked Beef",
                                "calories": 55,
                                "protein": 55,
                                "fat": 55,
                                "carbohydrates": 55,
                                "image_url": "55555",
                                "categorie": "meal-1"
                            }
                        ]
                }
            ]
}

到目前为止,嵌套的一天工作得很好,但嵌套的膳食内嵌套的日子没有任何想法如何解决这个问题?甚至终端内的日志也完全忽略它

您不需要在它们上循环并单独创建它们。Plan.new(plan_params)应该完成这项工作,您只需要正确指定嵌套属性。

变化:

  1. days to day_attributes
  2. 不需要传递id。
  3. 例如:

{
"name": "Test Plan",
"monthly_price": 0,
"image_url": null,
"day_attributes": [
            {
            "number": 1,
            "meal_attributes": [
                        {
                            "name": "Kale Salad",
                            "calories": null,
                            "protein": null,
                            "fat": null,
                            "carbohydrates": null,
                            "image_url": null,
                            "categorie": "snack-1"
                        },
                        {
                            "name": "Fit Burger",
                            "calories": null,
                            "protein": null,
                            "fat": null,
                            "carbohydrates": null,
                            "image_url": null,
                            "categorie": "meal-1"
                        },
                        {
                            "name": "Vegan Rataouille",
                            "calories": null,
                            "protein": null,
                            "fat": null,
                            "carbohydrates": null,
                            "image_url": null,
                            "categorie": "snack-2"
                        },
                        {
                            "name": "Chicken BBQ",
                            "calories": null,
                            "protein": null,
                            "fat": null,
                            "carbohydrates": null,
                            "image_url": null,
                            "categorie": "meal-3"
                        }
                    ]
            },
            {
            "number": 2,
            "meal_attributes": 
                    [
                        {
                            "name": "Woldrof Salad",
                            "calories": null,
                            "protein": null,
                            "fat": null,
                            "carbohydrates": null,
                            "image_url": null,
                            "categorie": "snack-1"
                        },
                        {
                            "name": "Baked Beef",
                            "calories": null,
                            "protein": null,
                            "fat": null,
                            "carbohydrates": null,
                            "image_url": null,
                            "categorie": "meal-1"
                        }
                    ]
            }
        ]

}

你也应该在permit参数中适当地提到它们,比如:

params.permit(:name, :monthly_price, :image_url, day_attributes: [:number, meal_attributes: [:name, :calories, :protein, :fat, :carbohydrates, :image_url, :categorie]])

更多信息请参考:

活动记录嵌套属性

Railscasts # 196

最新更新