从 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 响应中,任何不存在的值都不应在响应中发送。 当titlenull 时,我希望回复是:


    "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 禁用刷新

Django Rest Framework ModelViewSet 视图没有从数据表中看到 CSRFToken

Django Rest Framework:从 URL 参数动态设置数据库