在活动模型序列化器中..假设我有
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
这是我所能得到的最接近的结果:
# 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}}"