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

Posted

技术标签:

【中文标题】- 更新 - Rails API 应用程序使用来自控制器的 POST 请求一次创建不同模型的多个嵌套记录【英文标题】:- UPDATED - Rails API app create multiple nested records of a different models at once using POST request from the controller 【发布时间】:2021-09-30 16:39:27 【问题描述】:

我正在开发一个 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 个连接表

  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

中的 UPDATED 方法
# 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

这是我对 http://localhost:3000/api/create_custon_plan

POST 请求正文

    "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"
                            
                        ]
                
            ]

到目前为止,嵌套日工作正常,但嵌套日内的嵌套餐不知道如何解决这个问题?甚至终端内的日志也完全忽略它

【问题讨论】:

你能不能试着打印出day.valid?,还有像before_save这样的模型中有回调吗? 这有点不相关,但您应该将所有逻辑从控制器转移到模型中,该代码非常难以阅读,它在 JSON 响应中有很多条件和重复代码。您可能会受益于在 Active Record 事务块中编写所有内容,如果其中一个子对象插入失败,您可以回滚所有插入。 【参考方案1】:

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

变化:

    daysday_attributes 不需要传递 ID。

例如:


"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"
                        
                    ]
            
        ]

您还应该在许可参数中正确提及它们,例如:

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

更多信息可以参考:

Active Record Nested Attributes

Railscasts #196

【讨论】:

感谢您提供详细信息,但是我似乎在这里做错了,因为第 3 级没有保存,如果我在这里的示例,计划和日期按预期完美保存,但是第三级“餐”被控制台和json响应完全忽略,它总是一个空数组,有什么想法吗? 也许你可以看看表之间的关系应该是怎样的,因为我做了一些改变来优化数据库,class Plan &lt; ApplicationRecord has_and_belongs_to_many :days has_and_belongs_to_many :meals accepts_nested_attributes_for :meals accepts_nested_attributes_for :days endclass Day &lt; ApplicationRecord has_and_belongs_to_many :days has_and_belongs_to_many :meals endclass Meal &lt; ApplicationRecord has_and_belongs_to_many :days has_and_belongs_to_many :meals end 尝试将accepts_nested_attributes_for :mealsplan.rb移动到day.rb 你也可以参考 railscasts 的第 196 集,你会发现同样的例子。在上面添加了一个链接。 我尝试了很多其他方法,但没有任何效果,我正在检查 rails cast 剧集,也许它会有所帮助

以上是关于- 更新 - Rails API 应用程序使用来自控制器的 POST 请求一次创建不同模型的多个嵌套记录的主要内容,如果未能解决你的问题,请参考以下文章

如何从rails中的api更新用户模型?

Rails API 用户身份验证接受来自 React 代理的 api 调用,但不直接来自主机 url

根据我在 rails/react 应用程序上的 api 调用状态更新我的视图

来自 Github 的 gem rails 没有更新

使用 Rails 和 HTTParty 将 JSON 发布到 API

Rails JSON API 的简单令牌认证注销