有没有一种简单的方法可以只使用 Django Rest Framework 的 ModelSerializer 序列化非空字段?
Is there an easy way to only serialize non-empty fields with Django Rest Framework's ModelSerializer?
我正在开发一个 Django 项目,其中包含许多相当大的模型(大约 80 个字段)。我正在使用 Django Rest Framework 的 ModelSerializer 序列化模型,并使用 ViewSets 为我的前端提供 API。
效果很好,但我想减少服务器传输的数据量。我的大部分模型字段都是可选的,许多实例只有其中少数几个有值。在这些情况下 我只想序列化那些具有值(即真实值)的字段。
我想我可以在序列化器端或模型端做到这一点,但我不太明白这两者如何相互交流,可以这么说。
我目前的序列化器非常简单:
class OutfitSerializer(serializers.ModelSerializer):
class Meta:
model = Outfit
fields = '__all__'
观点同样简单:
# Outfit views
class OutfitViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Outfit.objects.all()
serializer = OutfitSerializer(queryset, many=True)
return Response(serializer.data)
我摆弄了序列化程序的子类并修改了 __init__ 函数(灵感来自 DRF 文档的 this 部分):
class NonEmptyFieldsModelSerializer(serializers.ModelSerializer):
"""
ModelSerializer that allows fields to be set at runtime via the
optional 'fields' argument
Copied from https://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields
"""
def __init__(self, *args, **kwargs):
super(NonEmptyFieldsModelSerializer, self).__init__(*args, **kwargs)
all_fields = set(self.fields)
for field_name in all_fields:
# IF THIS FIELD IS EMPTY IN THE OBJECT CURRENTLY BEING SERIALIZED:
self.fields.pop(field_name)
但我不确定如何以及是否可以访问 __init__ 中的当前对象。我也不太明白序列化整个查询集是如何工作的:Would a new serializer instance be initialized for each model instance?
我可以简单地为模型本身编写一个序列化程序函数,但这有点违背了使用 Django Rest Framework 的目的,因为我必须单独配置每个字段。
那么,我怎样才能只序列化模型实例的非空字段?
编辑:我还想删除值为 0 的小数。但是,DRF 的 ModelSerializer 默认将小数转换为字符串以避免不准确。因此,我调整如下:
class NonEmptySerializer(serializers.ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
non_null_ret = copy.deepcopy(ret)
for key in ret.keys():
if not ret[key]:
non_null_ret.pop(key)
elif isinstance(ret[key], str) and re.fullmatch('[0.]+', ret[key]):
non_null_ret.pop(key)
return non_null_ret
您可以覆盖 ModelSerializer 的 to_representation
方法:
class NonEmptySerializer(ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
non_null_ret = copy.deepcopy(ret)
for key in ret.keys():
if not ret[key]:
non_null_ret.pop(key)
return non_null_ret
然后在需要的时候继承这个序列化器:
class OutfitSerializer(NonEmptySerializer):
class Meta:
model = Outfit
fields = '__all__'
由于 to_representation
对单个序列化器和列表序列化器都被调用,因此它在这两种情况下都有效。
我正在开发一个 Django 项目,其中包含许多相当大的模型(大约 80 个字段)。我正在使用 Django Rest Framework 的 ModelSerializer 序列化模型,并使用 ViewSets 为我的前端提供 API。
效果很好,但我想减少服务器传输的数据量。我的大部分模型字段都是可选的,许多实例只有其中少数几个有值。在这些情况下 我只想序列化那些具有值(即真实值)的字段。
我想我可以在序列化器端或模型端做到这一点,但我不太明白这两者如何相互交流,可以这么说。
我目前的序列化器非常简单:
class OutfitSerializer(serializers.ModelSerializer):
class Meta:
model = Outfit
fields = '__all__'
观点同样简单:
# Outfit views
class OutfitViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Outfit.objects.all()
serializer = OutfitSerializer(queryset, many=True)
return Response(serializer.data)
我摆弄了序列化程序的子类并修改了 __init__ 函数(灵感来自 DRF 文档的 this 部分):
class NonEmptyFieldsModelSerializer(serializers.ModelSerializer):
"""
ModelSerializer that allows fields to be set at runtime via the
optional 'fields' argument
Copied from https://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields
"""
def __init__(self, *args, **kwargs):
super(NonEmptyFieldsModelSerializer, self).__init__(*args, **kwargs)
all_fields = set(self.fields)
for field_name in all_fields:
# IF THIS FIELD IS EMPTY IN THE OBJECT CURRENTLY BEING SERIALIZED:
self.fields.pop(field_name)
但我不确定如何以及是否可以访问 __init__ 中的当前对象。我也不太明白序列化整个查询集是如何工作的:Would a new serializer instance be initialized for each model instance?
我可以简单地为模型本身编写一个序列化程序函数,但这有点违背了使用 Django Rest Framework 的目的,因为我必须单独配置每个字段。
那么,我怎样才能只序列化模型实例的非空字段?
编辑:我还想删除值为 0 的小数。但是,DRF 的 ModelSerializer 默认将小数转换为字符串以避免不准确。因此,我调整
class NonEmptySerializer(serializers.ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
non_null_ret = copy.deepcopy(ret)
for key in ret.keys():
if not ret[key]:
non_null_ret.pop(key)
elif isinstance(ret[key], str) and re.fullmatch('[0.]+', ret[key]):
non_null_ret.pop(key)
return non_null_ret
您可以覆盖 ModelSerializer 的 to_representation
方法:
class NonEmptySerializer(ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
non_null_ret = copy.deepcopy(ret)
for key in ret.keys():
if not ret[key]:
non_null_ret.pop(key)
return non_null_ret
然后在需要的时候继承这个序列化器:
class OutfitSerializer(NonEmptySerializer):
class Meta:
model = Outfit
fields = '__all__'
由于 to_representation
对单个序列化器和列表序列化器都被调用,因此它在这两种情况下都有效。