Удалить пустые поля из ответа Django Rest Framework

Я разработал API, используя django-rest-framework. Я использую 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"
    }
}

Ответы

Ответ 1

Вы можете попробовать переопределить функцию 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

Я в основном скопировал базовую функцию to_native из serializers.BaseSerializer и добавил проверку значения.

ОБНОВЛЕНИЕ: Что касается DRF 3.0, to_native() был переименован в to_representation() и его реализация была немного изменена. Вот код для DRF 3.0, который игнорирует нулевые и пустые строковые значения:

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

Ответ 2

Ответ от CubeRZ не работал у меня, используя DRF 3.0.5. Я думаю, что метод to_native был удален и теперь заменен на_представление, определенное в 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 код с комментариями

Ответ 3

Я столкнулся с подобной проблемой и решил ее следующим образом:

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(list(filter(lambda x: x[1], ret.items())))
        return ret

Или, если вы хотите фильтровать только пустые поля, вы можете заменить лямбда-функцию следующим образом:

lambda x: x[1] is not None

Ответ 4

Я нашел это решение самым простым.

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])