从 Django Rest Framework 响应中删除空字段
Posted
技术标签:
【中文标题】从 Django Rest Framework 响应中删除空字段【英文标题】:Remove null fields from Django Rest Framework response 【发布时间】:2015-01-16 22:00:56 【问题描述】:我使用 django-rest-framework 开发了一个 API。 我正在使用 ModelSerializer 来返回模型的数据。
models.py
class MetaTags(models.Model):
title = models.CharField(_('Title'), max_length=255, blank=True, null=True)
name = models.CharField(_('Name'), max_length=255, blank=True, null=True)
serializer.py
class MetaTagsSerializer(serializers.ModelSerializer):
class Meta:
model = MetaTags
回复
"meta":
"title": null,
"name": "XYZ"
理想情况下,在 API 响应中,任何不存在的值都不应在响应中发送。
当title
是null
时,我希望回复是:
"meta":
"name": "XYZ"
【问题讨论】:
理想情况下,在 API 响应中任何不存在的值都不应该在响应中发送。是什么让你这么认为? 例如:Facebook graph api 仅返回 access_token 允许的配置文件数据。 @VincentBeltman Access_token 不允许与不存在完全不同。 这也是 GraphQL 和标准 REST API 工作方式的明显区别。 DRF 的目标不是 GraphQL。 【参考方案1】:我发现这个解决方案是最简单的。
from collections import OrderedDict
from rest_framework import serializers
class NonNullModelSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
result = super(NonNullModelSerializer, self).to_representation(instance)
return OrderedDict([(key, result[key]) for key in result if result[key] is not None])
【讨论】:
这个(和@asduj)是一个更好的解决方案。您应该始终避免将代码从库中复制到您的项目中,以调整某些行为。请注意,在 Python 3 中,您可以使用更简单的 super() 超级答案,很有用 绝妙的解决方案!谢谢! 谢谢你,西蒙【参考方案2】:我遇到了类似的问题,解决方法如下:
from operator import itemgetter
class MetaTagsSerializer(serializers.ModelSerializer):
class Meta:
model = MetaTags
def to_representation(self, instance):
ret = super().to_representation(instance)
# Here we filter the null values and creates a new dictionary
# We use OrderedDict like in original method
ret = OrderedDict(filter(itemgetter(1), ret.items()))
return ret
或者,如果您只想过滤空字段,您可以将 itemgetter(1)
替换为以下内容:
lambda x: x[1] is not None
【讨论】:
我知道这已经有很长一段时间了,但是两个 lambdas 不会产生相同的输出吗?他们有什么区别?我可能遗漏了什么。 要了解差异,试试这个:``` l = ['', 0, None, [], ()] print(list(filter(lambda x: x, l))) print( list(filter(lambda x: x is not None, l))) ``` 表达式x is not None
将只过滤 None,但只有 x
将过滤任何转换为 bool 为 False
的对象
我想你可能喜欢使用 operator.itemgetter(1) 而不是 lambda x: x[1] :)
您也可以跳过将过滤器转换为列表,因为 OrderedDict 可以使用可迭代操作。【参考方案3】:
CubeRZ 的答案对我不起作用,使用 DRF 3.0.5。我认为 to_native 方法已被删除,现在被 to_representation 取代,在 Serializer 中定义而不是 BaseSerializer。
我在 DRF 3.0.5 中使用了下面的类,它是来自 Serializer 的方法的副本,稍作修改。
from collections import OrderedDict
from rest_framework import serializers
from rest_framework.fields import SkipField
class NonNullSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
ret = OrderedDict()
fields = [field for field in self.fields.values() if not field.write_only]
for field in fields:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
if attribute is not None:
represenation = field.to_representation(attribute)
if represenation is None:
# Do not seralize empty objects
continue
if isinstance(represenation, list) and not represenation:
# Do not serialize empty lists
continue
ret[field.field_name] = represenation
return ret
EDIT 合并来自 cmets 的代码
【讨论】:
如果字段本身就是对象,我必须添加额外的验证:if attribute is not None: representation = field.to_representation(attribute) if representation is not None: ret[field.field_name] = representation
@db0 谢谢。这提醒我,我也想跳过空列表。
你能告诉我用法吗?我有这样的事情:gist.github.com/waqashamid/bd9bbfe08f5f9353b87bdcf86f2fbbaa。 Django 初学者。【参考方案4】:
您可以尝试覆盖 to_native 函数:
class MetaTagsSerializer(serializers.ModelSerializer):
class Meta:
model = MetaTags
def to_native(self, obj):
"""
Serialize objects -> primitives.
"""
ret = self._dict_class()
ret.fields = self._dict_class()
for field_name, field in self.fields.items():
if field.read_only and obj is None:
continue
field.initialize(parent=self, field_name=field_name)
key = self.get_field_key(field_name)
value = field.field_to_native(obj, field_name)
# Continue if value is None so that it does not get serialized.
if value is None:
continue
method = getattr(self, 'transform_%s' % field_name, None)
if callable(method):
value = method(obj, value)
if not getattr(field, 'write_only', False):
ret[key] = value
ret.fields[key] = self.augment_field(field, field_name, key, value)
return ret
我基本上从serializers.BaseSerializer
复制了基础 to_native 函数并添加了对值的检查。
更新:
至于 DRF 3.0,to_native()
被重命名为 to_representation()
并且它的实现做了一些改变。这是 DRF 3.0 的代码,它忽略了 null 和空字符串值:
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
ret = OrderedDict()
fields = self._readable_fields
for field in fields:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
# KEY IS HERE:
if attribute in [None, '']:
continue
# We skip `to_representation` for `None` values so that fields do
# not have to explicitly deal with that case.
#
# For related fields with `use_pk_only_optimization` we need to
# resolve the pk value.
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
if check_for_none is None:
ret[field.field_name] = None
else:
ret[field.field_name] = field.to_representation(attribute)
return ret
【讨论】:
这让我的回复小了大约 25 倍,耶!【参考方案5】:添加了两个选项:
删除值为 None 的键 删除值为 None 或 Blank 的键。from collections import OrderedDict
from rest_framework import serializers
# None field will be removed
class NonNullModelSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
result = super(NonNullModelSerializer, self).to_representation(instance)
return OrderedDict([(key, result[key]) for key in result if result[key] is not None])
# None & Blank field will be removed
class ValueBasedModelSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
result = super(ValueBasedModelSerializer, self).to_representation(instance)
return OrderedDict([(key, result[key]) for key in result if result[key] ])
简单地修改基于无和空白值的键删除,对于我 采用。感谢@Simon。
【讨论】:
以上是关于从 Django Rest Framework 响应中删除空字段的主要内容,如果未能解决你的问题,请参考以下文章
从 Django Rest Framework 响应中删除空字段
如何从 django-rest-framework 中的文件列表中过滤图像
使用 Django Rest Framework 将输入文件从 Vue.js 发送到 Django 时出现问题
django-rest-framework-simplejwt 禁用刷新