在使用 Django REST Framework 时,当序列化器嵌套在相关对象中时,如何排除父对象?
例如,我们有以下序列化器:
class EmployerSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Employer
fields = ('name', 'person')
class PersonSerializer(serializers.HyperlinkedModelSerializer):
employers = EmployerSerializer()
class Meta:
model = Person
fields = ('name', 'employers')
depth = 1
当我们访问 API http://0.0.0.0:8000/person/1/ 时,它会列出如下内容:
{
"name": "Joe Blow",
"employers": {
"name": "Acme Inc."
"person": "http://0.0.0.0:8000/person/1/"
}
}
可以看到,“employers” 的 “person” 键是自引用的,并且是冗余的,但仅当序列化器嵌套在它所引用的对象中时才会出现这种情况。似乎应该有一个选项可以在序列化器嵌套时排除它,但我们无法找到它。
2、解决方案
我们可以使用 DynamicFieldsModelSerializer
来动态控制序列化的字段。DynamicFieldsModelSerializer
是一个自定义的序列化器类,它允许我们在初始化时指定要显示的字段或要排除的字段。
首先,我们需要定义 DynamicFieldsModelSerializer
类:
class DynamicFieldsModelSerializer(serializers.HyperlinkedModelSerializer):
"""
A HyperlinkedModelSerializer that takes an additional `fields` argument that
controls which fields should be displayed.
"""
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
exclude = kwargs.pop('exclude', None)
# Instantiate the superclass normally
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
if fields:
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields.keys())
for field_name in existing - allowed:
self.fields.pop(field_name)
if exclude:
# Drop fields that are specified in the `exclude` argument.
excluded = set(exclude)
for field_name in excluded:
try:
self.fields.pop(field_name)
except KeyError:
pass
然后,我们需要扩展 EmployerSerializer
,使其继承 DynamicFieldsModelSerializer
:
class EmployerSerializer(DynamicFieldsModelSerializer):
class Meta:
model = Employer
fields = ('name', 'person')
最后,我们需要在 PersonSerializer
中使用 EmployerSerializer
,并将其中的 name
字段排除:
class PersonSerializer(serializers.HyperlinkedModelSerializer):
employers = EmployerSerializer(exclude=('name',))
class Meta:
model = Person
fields = ('name', 'employers')
depth = 1
现在,当我们访问 API http://0.0.0.0:8000/person/1/ 时,它将列出如下内容:
{
"name": "Joe Blow",
"employers": {
"person": "http://0.0.0.0:8000/person/1/"
}
}
可以看到,“employers” 的 “name” 字段已经被排除掉了。