繁体   English   中英

Rails 5.1 API - 如何允许嵌套 JSON 对象的属性的参数

[英]Rails 5.1 API - how to permit params for nested JSON object's attributes

关于这个主题至少有 10 个问题,但没有一个回答这个特定问题。 许多问题都与Rails的形式像这样,我没有,或者到比较复杂,像JSON结构, 还是这个


编辑关于接受的答案以及为什么这不是完全重复的

@CarlosRoque 的答案中的链接问题最初看起来是同一个问题,但它只解决了这个特定问题的 Rails 方面。

如果您阅读所有评论,您将看到多次尝试更改template_params方法,以使用“template_items_attributes”重命名或替换嵌套属性“template_items”。 这是必要的,因为 Rails 接受accepts_nested_attributes_for要求在名称后附加“_attributes”,否则它看不到它。

如果您检查该答案中的猴子补丁代码以修复 wrap_parameters 以便它适用于嵌套属性,您仍然会遇到问题,它实际上不会找到“template_items”(嵌套对象),因为它没有后缀“_attributes” ”。

因此,要完全解决这个问题,还必须修改客户端以将嵌套对象作为“template_items_attributes”发送。 对于 JS 客户端,这可以通过在对象上实现 toJSON() 方法以在序列化期间修改它来完成(示例在这里)。 但请注意,当您反序列化 JSON 时,您将需要手动创建该对象的实例,以便 toJSON() 工作( 在此处解释原因)。


我有一个简单的 has_many/belongs_to:

型号:

class Template < ApplicationRecord
  belongs_to :account
  has_many :template_items
  accepts_nested_attributes_for :template_items, allow_destroy: true
end


class TemplateItem < ApplicationRecord  
  belongs_to :template
  validates_presence_of :template

  enum item_type: {item: 0, heading: 1} 
end

客户端发送的 json 如下所示:

{
  "id": "55e27eb7-1151-439d-87b7-2eba07f3e1f7",
  "account_id": "a61151b8-deed-4efa-8cad-da1b143196c9",
  "name": "Test",
  "info": "INFO1234",
  "title": "TITLE1",
  "template_items": [
    {
      "is_completed": false,
      "item_type": "item"
    },
    {
      "is_completed": false,
      "item_type": "heading"
    }
  ]
}

有时在每个 template_item 中会有一个:id和一个:content属性(例如,在它们被创建并且用户开始编辑它们之后)。

template_params的方法templates_controller如下所示:

   params.require(:template).permit(
      :id, :account_id, :name, :title, :info, 
      template_items: [:id, :is_completed, :content, :item_type]
  )

如果这是一个 Rails 表单,那么该行将是:

   params.require(:template).permit(
      :id, :account_id, :name, :title, :info, 
      template_items_attributes: [:id, :is_completed, :content, :item_type]
  )

用于将嵌套的子对象保存为父模板更新操作的一部分。

我尝试更改嵌套参数名称:

def template_params
  params.require(:template).permit(:id, :account_id, :name, :title, :info, template_items: [:id, :is_completed, :content, :item_type])
  params[:template_items_attributes] = params.delete(:template_items) if params[:template_items]
  Rails.logger.info params
end

我可以看到它们仍然不被允许:

{  
      "template"   =><ActionController::Parameters   {  
      "id"      =>"55e27eb7-1151-439d-87b7-2eba07f3e1f7",
      "account_id"      =>"a61151b8-deed-4efa-8cad-da1b143196c9",
      "name"      =>"Test",
      "info"      =>"INFO1234",
      "title"      =>"TITLE1",
   }   permitted:false   >,
   "template_items_attributes"   =>   [  
      <ActionController::Parameters      {  
         "is_completed"         =>false,
         "item_type"         =>"item"
      }      permitted:false      >,
      <ActionController::Parameters      {  
         "is_completed"         =>false,
         "item_type"         =>"item"
      }      permitted:false      >
   ]
}

我也尝试合并:

template_params.merge! ({template_items_attributes:
params[:template_items]}) if params[:template_items].present?

同样的问题。

那么我怎样才能确保它们被允许并包含在 template_params 中而不只是做 .permit ! (即。我不想盲目地允许一切)?

控制器更新方法:

def update
    Rails.logger.info "*******HERE*******"
    Rails.logger.info template_params
    @template.template_items = template_params[:template_items_attributes]

    if @template.update(template_params)
      render json: @template
    else
      render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
    end
  end

UDPATE

如果我从客户端发送“template_items_attributes”而不是参数中的“template_items”到Rails,然后像这样执行推荐的template_params:

    def template_params
      params.require(:template).permit(:id, :account_id, :name, :title, :info, template_items_attributes: [:id, :is_completed, :content, :item_type])
    end

它仍然没有为模板创建新的孩子!

有了这个,我输出前后的参数,如下所示:

def update
    Rails.logger.info params
    Rails.logger.info "*******HERE*******"    
    Rails.logger.info template_params

    if @template.update(template_params)
      render json: @template
    else
      render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
    end
  end

这是这个场景的日志 - Rails 仍然完全忽略嵌入式数组。 请注意,就在 HERE 之前的 params 显示 allowed: false ,然后 template_params 不再包含子项“template_items_attributes”并标记为 allowed:true。

I, [2017-10-20T21:52:39.886104 #28142]  INFO -- : Processing by Api::TemplatesController#update as JSON
I, [2017-10-20T21:52:39.886254 #28142]  INFO -- :   Parameters: {"id"=>"55e27eb7-1151-439d-87b7-2eba07f3e1f7", "account_id"=>"a61151b8-deed-4efa-8cad-da1b143196c9", "name"=>"Test", "info"=>"INFO12345", "title"=>"TITLE1", "created_at"=>"2017-10-14T19:30:41.450Z", "updated_at"=>"2017-10-20T17:48:24.909Z", "template_items_attributes"=>[{"is_completed"=>false, "item_type"=>"item"}], "template"=>{"id"=>"55e27eb7-1151-439d-87b7-2eba07f3e1f7", "account_id"=>"a61151b8-deed-4efa-8cad-da1b143196c9", "name"=>"Test", "info"=>"INFO12345", "title"=>"TITLE1", "created_at"=>"2017-10-14T19:30:41.450Z", "updated_at"=>"2017-10-20T17:48:24.909Z"}}
D, [2017-10-20T21:52:39.903011 #28142] DEBUG -- :   User Load (7.7ms)  SELECT  "users".* FROM "users" WHERE "users"."uid" = $1 LIMIT $2  [["uid", "rmcsharry+owner@gmail.com"], ["LIMIT", 1]]
D, [2017-10-20T21:52:40.072148 #28142] DEBUG -- :   Template Load (1.4ms)  SELECT  "templates".* FROM "templates" WHERE "templates"."id" = $1 ORDER BY name ASC LIMIT $2  [["id", "55e27eb7-1151-439d-87b7-2eba07f3e1f7"], ["LIMIT", 1]]
I, [2017-10-20T21:52:40.083727 #28142]  INFO -- : <ActionController::Parameters {"id"=>"55e27eb7-1151-439d-87b7-2eba07f3e1f7", "account_id"=>"a61151b8-deed-4efa-8cad-da1b143196c9", "name"=>"Test", "info"=>"INFO12345", "title"=>"TITLE1", "created_at"=>"2017-10-14T19:30:41.450Z", "updated_at"=>"2017-10-20T17:48:24.909Z", "template_items_attributes"=>[{"is_completed"=>false, "item_type"=>"item"}], "controller"=>"api/templates", "action"=>"update", "template"=>{"id"=>"55e27eb7-1151-439d-87b7-2eba07f3e1f7", "account_id"=>"a61151b8-deed-4efa-8cad-da1b143196c9", "name"=>"Test", "info"=>"INFO12345", "title"=>"TITLE1", "created_at"=>"2017-10-14T19:30:41.450Z", "updated_at"=>"2017-10-20T17:48:24.909Z"}} permitted: false>
I, [2017-10-20T21:52:40.083870 #28142]  INFO -- : *******HERE*******
D, [2017-10-20T21:52:40.084550 #28142] DEBUG -- : Unpermitted parameters: :created_at, :updated_at
I, [2017-10-20T21:52:40.084607 #28142]  INFO -- : <ActionController::Parameters {"id"=>"55e27eb7-1151-439d-87b7-2eba07f3e1f7", "account_id"=>"a61151b8-deed-4efa-8cad-da1b143196c9", "name"=>"Test", "title"=>"TITLE1", "info"=>"INFO12345"} permitted: true>
D, [2017-10-20T21:52:40.084923 #28142] DEBUG -- : Unpermitted parameters: :created_at, :updated_at
D, [2017-10-20T21:52:40.085375 #28142] DEBUG -- :    (0.2ms)  BEGIN
D, [2017-10-20T21:52:40.114015 #28142] DEBUG -- :   Account Load (1.2ms)  SELECT  "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2  [["id", "a61151b8-deed-4efa-8cad-da1b143196c9"], ["LIMIT", 1]]
D, [2017-10-20T21:52:40.131895 #28142] DEBUG -- :   Template Exists (0.8ms)  SELECT  1 AS one FROM "templates" WHERE "templates"."name" = $1 AND ("templates"."id" != $2) AND "templates"."account_id" = 'a61151b8-deed-4efa-8cad-da1b143196c9' LIMIT $3  [["name", "Test"], ["id", "55e27eb7-1151-439d-87b7-2eba07f3e1f7"], ["LIMIT", 1]]
D, [2017-10-20T21:52:40.133754 #28142] DEBUG -- :    (0.3ms)  COMMIT
D, [2017-10-20T21:52:40.137763 #28142] DEBUG -- :   CACHE Account Load (0.0ms)  SELECT  "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2  [["id", "a61151b8-deed-4efa-8cad-da1b143196c9"], ["LIMIT", 1]]
D, [2017-10-20T21:52:40.138714 #28142] DEBUG -- :    (0.2ms)  BEGIN
D, [2017-10-20T21:52:40.141293 #28142] DEBUG -- :   User Load (1.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 FOR UPDATE  [["id", "88de3be9-6d18-4687-ab80-d50f78638ca9"], ["LIMIT", 1]]
D, [2017-10-20T21:52:40.235163 #28142] DEBUG -- :   Account Load (0.7ms)  SELECT  "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2  [["id", "a61151b8-deed-4efa-8cad-da1b143196c9"], ["LIMIT", 1]]
D, [2017-10-20T21:52:40.240997 #28142] DEBUG -- :   SQL (1.4ms)  UPDATE "users" SET "tokens" = $1, "updated_at" = $2 WHERE "users"."id" = $3  [["tokens", "{\"ryyymFZ7fpH50rMKArjZ2Q\":{\"token\":\"$2a$10$4jkgRe4LBPxJ8fQUOKCSausUi7DbIUD0bE.7ZRoOuTHrRuX6CaWOe\",\"expiry\":1509293414,\"last_token\":\"$2a$10$cpI.mz81JFjQT0J9acCCl.NdrEatI5l17GtrwrAfwyhyN3xRExcaC\",\"updated_at\":\"2017-10-15T17:10:16.996+02:00\"},\"Y2y0maUT5WYSfH6VZeORag\":{\"token\":\"$2a$10$8KERiIwlc3rX.Mdu.CW6wOMLDbVyB2PFCaBIlw7/LUxC3ITpYTISW\",\"expiry\":1509293475,\"last_token\":\"$2a$10$r6Xw6798T1P7UZlTbEaXoeBCl9oK2fMs72ppAtars8Ai/kaE6nE66\",\"updated_at\":\"2017-10-15T17:11:18.066+02:00\"},\"9Cy48CPVj3WhFkEBPUZQ1Q\":{\"token\":\"$2a$10$Qy4JOD8.jIcPhf93MqFCIelnVaA/ssE31w5DlL8MShDuMROsLSNuS\",\"expiry\":1509293942,\"last_token\":\"$2a$10$e6sxklrHRRD1C15Ix/MqQOfACuCMznmzUjF296cpO1ypWVvJ.JFJK\",\"updated_at\":\"2017-10-15T17:19:05.200+02:00\"},\"O5iufW0Gacqs9sIfJ9705w\":{\"token\":\"$2a$10$EkDf7.y3lY9D36lAwNHBGuct97M6/HGDvnrUsD72c8zCsfVd8y9c2\",\"expiry\":1509482450,\"last_token\":\"$2a$10$S0kHEvKxom2Qgdy0r.q0aeTSlSBFkqU4XZeY91n3RkkYkQykmmGVi\",\"updated_at\":\"2017-10-17T21:40:50.300+02:00\"},\"ETOadoEtoxcz6rR6Ced_dA\":{\"token\":\"$2a$10$8t01bWv/PsVojs3cazuSg..FWa9SZwq1/PUDfuN1S4yBxnMFv2zre\",\"expiry\":1509742360,\"last_token\":\"$2a$10$hveuajISXDOjHLm9EkVzvOd3pwKkqE1rQnIFBoojf0vgMLXV2EvVe\",\"updated_at\":\"2017-10-20T21:52:40.233+02:00\"}}"], ["updated_at", "2017-10-20 19:52:40.236607"], ["id", "88de3be9-6d18-4687-ab80-d50f78638ca9"]]
D, [2017-10-20T21:52:40.243960 #28142] DEBUG -- :    (1.3ms)  COMMIT
I, [2017-10-20T21:52:40.244504 #28142]  INFO -- : Completed 200 OK in 358ms (Views: 1.0ms | ActiveRecord: 37.7ms)

我认为您忘记了 params.require(:template).permit( ... 是一种返回值的方法,当您稍后调用 params 对其进行修改时,您只是在修改尚未被允许的参数。您想要什么要做的是交换执行参数操作时的顺序。

def template_params
  params[:template][:template_items_attributes] = params[:template_items_attributes]
  params.require(:template).permit(:id, :account_id, :name, :title, :info, template_items_attributes: [:id, :is_completed, :content, :item_type])
end

更新: wrap_parameters 是罪魁祸首,因为它没有在包装的参数中包含嵌套参数。 这解决了这个问题

更新:此答案实现了不同的解决方案Rails 4 Not Updating Nested Attributes Via JSON

这是github中的一个长期开放请求!! 疯狂https://github.com/rails/rails/pull/19254

更新:这最终合并到 AR 6 https://github.com/rails/rails/commit/62b7ad46c0f3ff24980956daadba46ccb2568445

问题您的问题出在您的update操作中,您试图在尚未构建的@template上保存关联。 因为散列中没有“id”,更新函数只是忽略它们。

解决的办法是,反覆在未来通过该协会的哈希值的阵列update行动,并建立他们在@template调用之前update@template

这是 sudo 代码(没试过,所以不要复制粘贴):

模型

class Template < ApplicationRecord
  belongs_to :account
  has_many :template_items
  accepts_nested_attributes_for :template_items, allow_destroy: true
end


class TemplateItem < ApplicationRecord  
  belongs_to :template, optional:true  # <------ CHANGE
  validates_presence_of :template

  enum item_type: {item: 0, heading: 1} 
end

强参数定义

params.require(:template).permit(
      :id, :account_id, :name, :title, :info, 
      template_items_attributes: [:id, :is_completed, :content, :item_type]
  )

更新操作

def update

    template_params.template_items.each do |item_hash| # <------ CHANGE
        @template.template_items.build(item_hash)
    end

    if @template.update(template_params)
      render json: @template
    else
      render json: ErrorSerializer.serialize(@template.errors), status: :unprocessable_entity
    end
 end
 def  template_params 
    template_params = params.require(:template).permit(:id, :account_id, :name,:title, :info, template_items: [:id, :is_completed, :content, :item_type])
    template_params[:template_items_attributes] = template_params.delete :template_items      
    template_params.permit!
end

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM