【问题标题】:How can I receive a JSON:API post of nested attributes in Ruby on Rails?如何在 Ruby on Rails 中接收 JSON:API 嵌套属性帖子?
【发布时间】:2019-10-04 02:41:07
【问题描述】:

Rails 中是否有“标准”方法来接收(可能嵌套)JSON:API POST 对象?

JSON:API 规范对 GET / POST / PUT 等使用相同的格式,但 rails 似乎需要 *_attributes 和 accept_nested_attributes_for。这些似乎不兼容。

我觉得我正在做的事情一定很常见,但我在查找文档时遇到了麻烦。我想使用一个使用 JSON:API 规范与 Rails 应用程序通信的 React/Redux 应用程序。我只是不确定如何处理嵌套关联。

【问题讨论】:

  • 有多个 gem 可以处理 JSON:API 的序列化/反序列化。它们都不能被视为“标准”,因为它们不是 Rails 核心的一部分。 ruby-toolbox.com/…
  • 没有一个真正能直接使用accepts_nested_attributes,它是专门为rails表单助手量身定制的。如果要使用 accepts_nested_attributes,则必须将哈希/哈希数组转换为预期的 assocation_name_attribute(s)

标签: ruby-on-rails reactjs redux json-api


【解决方案1】:

您可以active_model_serializer gem 的反序列化功能。

来自宝石的docs

class PostsController < ActionController::Base
  def create
    Post.create(create_params)
  end

  def create_params
    ActiveModelSerializers::Deserialization.jsonapi_parse(params, only: [:title, :content, :author])
  end
end

以上内容可以与以下 JSON API 有效负载一起使用:

document = {
  'data' => {
    'id' => 1,
    'type' => 'post',
    'attributes' => {
      'title' => 'Title 1',
      'date' => '2015-12-20'
    },
    'relationships' => {
      'author' => {
        'data' => {
          'type' => 'user',
          'id' => '2'
        }
      },
      'second_author' => {
        'data' => nil
      },
      'comments' => {
        'data' => [{
          'type' => 'comment',
          'id' => '3'
        },{
          'type' => 'comment',
          'id' => '4'
        }]
      }
    }
  }
}

无需指定任何选项即可解析整个文档:

ActiveModelSerializers::Deserialization.jsonapi_parse(document)
#=>
# {
#   title: 'Title 1',
#   date: '2015-12-20',
#   author_id: 2,
#   second_author_id: nil
#   comment_ids: [3, 4]
# }

【讨论】:

  • Kartikey Tanna 你能展示一下反序列化后的样子吗?
  • @pixelearth 它在文档中,但我更新了答案,所以很明显
  • 我最感兴趣的是这种反序列化是否适用于 *_attributes 和 accept_nested_attributes_for。文档没有显示这一点。
  • 它确实让我感到困惑的一件事是:'Attributes/Json 这些适配器目前没有反序列化'。也许这意味着不可能?
  • @pixelearth AMS 有不同的适配器。我们正在讨论的一个是json_api,它支持反序列化。其他适配器 attributesjson 不这样做。
【解决方案2】:

几天前我在 JSON:API repo 上看到了这些冗长的线程/问题讨论 #979#795,显然 JSON API 似乎没有针对 accepts_nested_attributes_for 的真正解决方案。

我不知道这是否是更好的解决方案,但解决方法是为您的 belongs_tohas_many/has_one 关联设置路由。

类似的东西:

您的routes.rb

Rails.application.routes.draw do
  resources :contacts do
    resource :kind, only: [:show]
    resource :kind, only: [:show], path: 'relationships/kind'

    resource :phones, only: [:show]
    resource :phones, only: [:show], path: 'relationships/phones'

    resource :phone, only: [:update, :create, :destroy]
    # These relationships routes is merely a suggestion of a best practice
    resource :phone, only: [:update, :create, :destroy], path: 'relationships/phone'

    resource :address, only: [:show, :update, :create, :destroy]
    resource :address, only: [:show, :update, :create, :destroy], path: 'relationships/address'
  end

  root 'contacts#index'
end

他们实现你的控制器phones_controller.rb 跟上面的例子一样:

class PhonesController < ApplicationController
  before_action :set_contacts

  def update
    phone = Phone.find(phone_params[:id])

    if phone.update(phone_params)
      render json: @contact.phones, status: :created, location: contact_phones_url(@contact.id)
    else
      render json: @contact.errors, status: :unprocessable_entity
    end
  end

  # DELETE /contacts/1/phone
  def destroy
    phone = Phone.find(phone_params[:id])
    phone.destroy
  end

  # POST contacts/1/phone
  def create
    @contact.phones << Phone.new(phone_params)

    if @contact.save
      render json: @contact.phones, status: :created, location: contact_phones_url(@contact.id)
    else
      render json: @contact.errors, status: :unprocessable_entity
    end
  end

  # GET /contacts/1/phones
  def show
    render json: @contact.phones
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_contacts
      @contact = Contact.find(params[:contact_id])
    end

    def phone_params
      ActiveModelSerializers::Deserialization.jsonapi_parse(params)
    end
end

通过这样做,您应该能够通过单独的联系人正常地请求电话POST,如下所示:

http://localhost:3000/contacts/1/phone 上发布正文:

{
    "data": {
            "type": "phones",
            "attributes": {
                "number": "(+55) 91111.2222"
            }
    }
}

http://localhost:3000/contacts/1/phones 上的响应或 GET:

{
    "data": [
        {
            "id": "40",
            "type": "phones",
            "attributes": {
                "number": "(55) 91111.2222"
            },
            "relationships": {
                "contact": {
                    "data": {
                        "id": "1",
                        "type": "contacts"
                    },
                    "links": {
                        "related": "http://localhost:3000/contacts/1"
                    }
                }
            }
        }
    ]
}

希望回答

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-25
    • 2014-02-05
    • 1970-01-01
    相关资源
    最近更新 更多