Remove null fields from Django Rest Framework response

22,333

Solution 1

I found this solution to be the simplest.

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

Solution 2

I faced a similar problem and solved it as follows:

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

Or if you want to filter only empty fields you can replace the itemgetter(1) by the following:

lambda x: x[1] is not None

Solution 3

The answer from CubeRZ didn't work for me, using DRF 3.0.5. I think the method to_native has been removed and is now replaced by to_representation, defined in Serializer instead of BaseSerializer.

I used the class below with DRF 3.0.5, which is a copy of the method from Serializer with a slight modification.

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 incorporated code from comments

Solution 4

You could try overriding the to_native function:

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

I basically copied the base to_native function from serializers.BaseSerializer and added a check for the value.

UPDATE: As for DRF 3.0, to_native() was renamed to to_representation() and its implementation was changed a little. Here's the code for DRF 3.0 which ignores null and empty string values:

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
Share:
22,333

Related videos on Youtube

Dev Rishi Khare
Author by

Dev Rishi Khare

Updated on July 09, 2022

Comments

  • Dev Rishi Khare
    Dev Rishi Khare almost 2 years

    I have developed an API using django-rest-framework. I am using ModelSerializer to return data of a model.

    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
    

    response

    {
        "meta": {
            "title": null,
            "name": "XYZ"
        }
    }
    

    Ideally in an API response any value which is not present should not be sent in the response. When the title is null I want the response to be:

    {
        "meta": {
            "name": "XYZ"
        }
    }
    
    • Vincent Beltman
      Vincent Beltman over 9 years
      Ideally in an API response any value which is not present should not be sent in the response. What makes you think that?
    • Dev Rishi Khare
      Dev Rishi Khare over 9 years
      For example: Facebook graph api returns only that data of a profile which is allowed by the access_token. @VincentBeltman
    • Vincent Beltman
      Vincent Beltman over 9 years
      Not allowed by an access_token is totally different than not exsisting.
    • maco
      maco over 2 years
      That's also a distinct difference in how GraphQL and standard REST APIs work. DRF isn't aiming for GraphQL.
  • db0
    db0 almost 9 years
    I had to add an extra verification in case the fields are objects themselves: if attribute is not None: representation = field.to_representation(attribute) if representation is not None: ret[field.field_name] = representation
  • David R.
    David R. almost 9 years
    @db0 Thanks. This reminded me that I would want to skip empty lists as well.
  • Sean Francis N. Ballais
    Sean Francis N. Ballais about 6 years
    I know this has been a long while already but wouldn't both lambdas produce the same output? What are their differences? I might be missing something.
  • asduj
    asduj about 6 years
    To understand difference try this: ``` l = ['', 0, None, [], ()] print(list(filter(lambda x: x, l))) print(list(filter(lambda x: x is not None, l))) ``` Expression x is not None will filter only None, but only x will filter any object that converts to bool as False
  • waqasgard
    waqasgard about 6 years
    Can you tell me the usage? I have something like this : gist.github.com/waqashamid/bd9bbfe08f5f9353b87bdcf86f2fbbaa. Beginner at Django.
  • rgov
    rgov almost 6 years
    This (and @asduj's) is a way better solution. You should always avoid copying code from a library into your project just to tweak some behavior. Note that in Python 3 you can use the simpler super()
  • Samuel Blattner
    Samuel Blattner almost 5 years
    This made my response about 25 times smaller, yay!
  • Nikita Hismatov
    Nikita Hismatov over 4 years
    I think you may like using operator.itemgetter(1) instead of lambda x: x[1] :)
  • Nikita Hismatov
    Nikita Hismatov over 4 years
    You may also skip converting filter to list, because OrderedDict can operate with an iterable.
  • Alex
    Alex over 3 years
    Super answer, very useful
  • MCFreddie777
    MCFreddie777 over 3 years
    Wonderful solution! Thank you!
  • Chandan Sharma
    Chandan Sharma almost 3 years
    Thanks you simon