【问题标题】:How to format Json to not nest associations with ActiveModel::Serializer如何格式化 Json 以不与 ActiveModel::Serializer 嵌套关联
【发布时间】:2021-10-09 23:18:39
【问题描述】:

我在 Rails 应用程序中使用 ActiveModel::Serializer 将我的模型数据格式化为 json 响应,但我想更改格式以便我的主模型的关联不嵌套。我尝试设置 root: false 但这不起作用

预期行为与实际行为

我有一个模型 Accountbelongs_to :account_status 关联 我能够在AccountSerializer 中添加这个关联来获得相关的数据就好了。但是按照我的 api 合同要求,我需要在没有关联嵌套的情况下格式化 json。

所以我得到了这个:

{
   "account_id": 1
   <other account info>
   ...
   "account_status": {
      "status_code": 1
      "desc": "status description"
      ....
   }
}

但我想要这个:

{
   "account_id": 1
   <other account info>
   ...
   "account_status_status_code": 1
   "account_status_desc": "status description"
   ....
}

模型+序列化代码

如何在不将每个 account_status 字段作为单独属性写入 AccountSerializer 的情况下实现预期行为??

控制器

class AccountsController < ActionController::API
  def show
    account = Account.find(params[:account_id])
    render json: account
  end
end

型号

class Account < ActiveRecord::Base
  self.primary_key = :account_id

  belongs_to :account_status, foreign_key: :account_status_code, inverse_of: :accounts

  validates :account_status_code, presence: true
end

序列化器

class AccountSerializer < ActiveModel::Serializer
  attributes(*Account.attribute_names.map(&:to_sym))

  belongs_to :account_status, 
             foreign_key: :account_status_code,
             inverse_of: :accounts
end

环境

操作系统类型和版本:macOS Catalina v 10.15.7 Rails 6.1.4

ActiveModelSerializers 版本 0.10.0

ruby -e "puts RUBY_DESCRIPTION" 的输出: ruby 3.0.2p107(2021-07-07 修订版 0db68f0233)[x86_64-darwin19]

【问题讨论】:

  • validates :account_status_code, presence: true 是多余的,因为 belongs_to 关联在 Rails 5.1+ 中默认是非可选的。这将导致两个验证错误,这很可能是不可取的。

标签: ruby-on-rails json ruby serialization active-model-serializers


【解决方案1】:

你可以用下面的代码替换belongs_to :account_status,...

class AccountSerializer < ActiveModel::Serializer
  attributes(*Account.attribute_names.map(&:to_sym))
  
  AccountStatus.attribute_names.each do |attr_name|
    key = "account_status_#{attr_name}"
    define_method(key) do
     # i checked and see that `object.account_status` just call one time
     object.account_status.send(attr_name)
    end
    attribute key.to_sym   
  end
end

【讨论】:

【解决方案2】:

您可以在模型中使用setup delegation,而不是在序列化程序中使用关联:

class Account < ApplicationRecord
  delegate :desc, ..., 
    to: :account_status,
    prefix: true
end

这将创建一个account_status_desc 方法,您可以简单地从您的序列化程序中调用它:

class AccountSerializer < ActiveModel::Serializer
  attributes(
    :foo, 
    :bar, 
    :baz,
    :account_status_code, # this is already a attribute of Account
    :account_status_desc
    # ...
  )
end

另一种方法是向序列化程序添加方法:

class AccountSerializer < ActiveModel::Serializer
  attributes :foo

  def foo
    object.bar.do_something
  end
end

当序列化的结果与模型中的内部表示不一致时,这是一个不错的选择。

【讨论】:

  • 我认为*Account.attribute_names.map(&amp;:to_sym) 是一个潜在的安全问题。虽然方便,但它可能会暴露不应暴露的数据。就像参数白名单一样,您应该只使用客户端应该能够访问的明确白名单,而不是危险的魔法。
猜你喜欢
  • 2018-10-07
  • 2017-11-01
  • 1970-01-01
  • 2014-12-05
  • 1970-01-01
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多