Ruby on rails 如何通过Rails API接受JSON而不使用_属性“;对于嵌套字段

Ruby on rails 如何通过Rails API接受JSON而不使用_属性“;对于嵌套字段,ruby-on-rails,json,Ruby On Rails,Json,我已经用Rails编写了一个API,需要在API调用中接受一些嵌套的_属性 目前我通过电子邮件发送数据 PATCH/api/v1/vintages/1.json { "vintage": { "year": 2014, "name": "Some name", "foo": "bar", "sizes_attributes": [ { "name": "large", "quantity": "12" }

我已经用Rails编写了一个API,需要在API调用中接受一些嵌套的_属性

目前我通过电子邮件发送数据

PATCH/api/v1/vintages/1.json

{
  "vintage": {
    "year": 2014,
    "name": "Some name",
    "foo": "bar",
    "sizes_attributes": [
      {
        "name": "large",
        "quantity": "12"
      },
      {
        "name": "medium",
        "quantity": "2"
      }
    ]
  }
}
{
  "vintage": {
    "year": 2014,
    "name": "Some name",
    "foo": "bar",
    "sizes": [
      {
        "name": "large",
        "quantity": "12"
      },
      {
        "name": "medium",
        "quantity": "2"
      }
    ]
  }
}
但是,我想执行以下操作:

PATCH/api/v1/vintages/1.json

{
  "vintage": {
    "year": 2014,
    "name": "Some name",
    "foo": "bar",
    "sizes_attributes": [
      {
        "name": "large",
        "quantity": "12"
      },
      {
        "name": "medium",
        "quantity": "2"
      }
    ]
  }
}
{
  "vintage": {
    "year": 2014,
    "name": "Some name",
    "foo": "bar",
    "sizes": [
      {
        "name": "large",
        "quantity": "12"
      },
      {
        "name": "medium",
        "quantity": "2"
      }
    ]
  }
}
区别在于属性是字段键的一部分。我希望能够
为:size
接受\u嵌套的\u属性,而不必使用
\u属性
作为JSON对象的一部分


有人知道如何管理吗?

您可以自由地在强参数方法中执行一些魔术。根据您的需要,您的控制器中可能有以下方法:

def vintage_params
  params.require(:vintage).permit(:year, :name, :foo, { sizes: [:name, :quantity] })
end
module PrettyApi
  class << self
    def with_nested_attributes(params, attrs)
      return if params.blank?

      case attrs
      when Hash
        with_nested_hash_attributes(params, attrs)
      when Array
        with_nested_array_attributes(params, attrs)
      when String, Symbol
        unless params[attrs].blank?
          params["#{attrs}_attributes"] = params.delete attrs
        end
      end
      params
    end

    private

    def with_nested_hash_attributes(params, attrs)
      attrs.each do |k, v|
        with_nested_attributes params[k], v
        with_nested_attributes params, k
      end
    end

    def with_nested_array_attributes(params, attrs)
      params.each do |np|
        attrs.each do |v|
          with_nested_attributes np, v
        end
      end
    end
  end
end
您需要做的就是调整该方法中
size
键的名称。我建议:

def vintage_params
  vintage_params = params.require(:vintage).permit(:year, :name, :foo, { sizes: [:name, :quantity] })
  vintage_params[:sizes_attributes] = vintage_params.delete :sizes
  vintage_params.permit!
end

这将删除
:size
键,并将其放入预期的
:size\u属性中,而不会弄乱漂亮的json。您无法直接使用
接受嵌套的属性来更改名称。
我也在寻找一种方法,避免嵌套的属性污染我的RESTful API。我想我会分享我的解决方案,因为它足够通用,对遇到相同问题的任何人都有用。它从一个简单的模块开始,该模块可从控制器中利用:

def vintage_params
  params.require(:vintage).permit(:year, :name, :foo, { sizes: [:name, :quantity] })
end
module PrettyApi
  class << self
    def with_nested_attributes(params, attrs)
      return if params.blank?

      case attrs
      when Hash
        with_nested_hash_attributes(params, attrs)
      when Array
        with_nested_array_attributes(params, attrs)
      when String, Symbol
        unless params[attrs].blank?
          params["#{attrs}_attributes"] = params.delete attrs
        end
      end
      params
    end

    private

    def with_nested_hash_attributes(params, attrs)
      attrs.each do |k, v|
        with_nested_attributes params[k], v
        with_nested_attributes params, k
      end
    end

    def with_nested_array_attributes(params, attrs)
      params.each do |np|
        attrs.each do |v|
          with_nested_attributes np, v
        end
      end
    end
  end
end
模块PrettyApi

类好了,这将是一种黑客行为,但不能只做一些
params[:vintage][:size\u attributes]=params[:vintage][:size]
来重命名它们。“你不能直接用accepts\u nested\u attributes\u来更改名称。”这是正确的,也是不幸的。我也有同样的问题,对我不起作用。参数已正确重命名,但当我尝试执行
Model.new(Params)
时,我得到
ActiveModel::禁止属性错误
。在将属性分配给模型实例之前,似乎还有一些额外的检查。您可能需要执行
vintage_params.permit
作为Rails 4.2及更高版本中的最后一行。@ptd感谢您提供此解决方案!我想提一下我遇到的一个问题,其他人也可能遇到。如果您发送的请求正文中没有包含
size
参数,您的模型可能会遇到问题,抱怨
size\u属性不能为零,例如,如果它需要数组。(显然这取决于您的型号。)我可以通过检查参数是否提供来解决这个问题:
vintage\u params[:size\u attributes]=vintage\u params.delete:size if vintage\u params.key?(:size)
对我有效,谢谢。希望您能提供一些说明,因为它并不能自我解释这是如何工作的。对于一个已经存在这么久的框架来说,所有这些解决方案都感觉有点粗糙。当然,在2020年,从rails api获取嵌套json、在前端对其进行轻微操作并将其发送回控制器(无需附加嵌套属性)是一种最佳实践@Branksy发布上述解决方案五年后,我认为嵌套属性不应该用于REST/GraphQLAPI。因此,我不认为这是框架的错误,而是像我这样的开发人员错误地使用了这个特性。现在,我主张发送清晰描述数据的JSON,并在后端使用服务对象找出存储数据的位置和方式。