为活动模型序列化器关联选择特定字段

问题描述 投票:0回答:1

活动模型序列化器中..假设我有

CompanySerializer
EmployeeSerializer
。 在
CompanySerializer
上,我有一个字段
ceo
,我想在其中呈现与
EmployeeSerializer
的关联,但仅显示 EmployeeSerializer 中字段的
子集

就本示例而言,假设

EmployeeSerializer
具有属性
name
homepage
salary
,但在
CompanySerializer.ceo
上我们只想显示
name
homepage

我想避免重新定义新的

CompanyEmployeeSerializer
类,如果我可以“选择”我想要用于内部
CompanySerializer
员工协会的字段。

我还想避免运行

EmployeeSerializer
然后针对特定字段进行切片的解决方案。 这样做的原因是我想避免运行与序列化不需要的字段相关的代码。

注意:这与如何根据条件隐藏序列化器中的某些字段的问题不同。 这是关于如何从另一个序列化器调用序列化器并获取特定字段。

如果存在这样的事情,我会很高兴:

CompanySerializer < ActiveModel::Serializer
  belongs_to :ceo, serializer: EmployeeSerializer, fields: [:name, :homepage]
end
ruby-on-rails associations active-model-serializers activemodel
1个回答
0
投票

这是我所能得到的最接近的结果:

# app/serializers/company_serializer.rb

class CompanySerializer < ActiveModel::Serializer
  belongs_to :ceo do
    EmployeeSerializer.new(object.ceo).attributes([:name, :homepage])
  end
end

你可以这样做吗?

# app/serializers/company_serializer.rb

class CompanySerializer < ActiveModel::Serializer
  belongs_to :ceo, serializer: EmployeeSerializer, fields: [:name]
end

你必须深入挖掘:

$ git diff
  diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb
  index 5e34779..e50f44f 100644
  --- a/lib/active_model/serializer.rb
  +++ b/lib/active_model/serializer.rb
  @@ -367,7 +367,9 @@ module ActiveModel
         adapter_options ||= {}
         options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
         if (fieldset = adapter_options[:fieldset])
  -        options[:fields] = fieldset.fields_for(json_key)
  +        if fields = fieldset.fields_for(json_key)
  +          options[:fields] = fields
  +        end
         end
         resource = attributes_hash(adapter_options, options, adapter_instance)
         relationships = associations_hash(adapter_options, options, adapter_instance)
  diff --git a/lib/active_model/serializer/association.rb b/lib/active_model/serializer/association.rb
  index 7aeee33..ce0cd29 100644
  --- a/lib/active_model/serializer/association.rb
  +++ b/lib/active_model/serializer/association.rb
  @@ -55,7 +55,8 @@ module ActiveModel
           association_object = association_serializer && association_serializer.object
           return unless association_object

  -        serialization = association_serializer.serializable_hash(adapter_options, {}, adapter_instance)
  +        options = {fields: reflection.options[:fields]}.compact
  +        serialization = association_serializer.serializable_hash(adapter_options, options, adapter_instance)

           if polymorphic? && serialization
             polymorphic_type = association_object.class.name.underscore

猴子补丁来尝试一下:

# config/initializers/serializer_patch.rb

ActiveModel::Serializer::Association.class_eval do
  def serializable_hash(adapter_options, adapter_instance)
    association_serializer = lazy_association.serializer
    return virtual_value if virtual_value
    association_object = association_serializer && association_serializer.object
    return unless association_object

    # pass `fields:` option from the reflection to serializer
    options = {fields: reflection.options[:fields]}.compact
    serialization = association_serializer.serializable_hash(adapter_options, options, adapter_instance)

    if polymorphic? && serialization
      polymorphic_type = association_object.class.name.underscore
      serialization = {type: polymorphic_type, polymorphic_type.to_sym => serialization}
    end
    serialization
  end
end

ActiveModel::Serializer.class_eval do
  def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance)
    adapter_options ||= {}
    options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
    if (fieldset = adapter_options[:fieldset])

      # fix controller `fields:` always overriding our patched fields option 
      # even if you don't pass `fields:` to `render`:
      if fields = fieldset.fields_for(json_key)
        options[:fields] = fields
      end

    end
    resource = attributes_hash(adapter_options, options, adapter_instance)
    relationships = associations_hash(adapter_options, options, adapter_instance)
    resource.merge(relationships)
  end
end

测试:

>> CompaniesController.renderer.render(json: Company.first)
=> "{\"id\":1,\"name\":null,\"ceo\":{\"name\":null}}"
© www.soinside.com 2019 - 2024. All rights reserved.