Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework to return dicts instead of OrderedDicts

I have a serializer with two attached SerializerMethodField fields:

class BentoSerializer(ModelSerializer):
    zones = SerializerMethodField()
    lead_zone = SerializerMethodField()

    def get_zones(self, obj):
        zone_queryset = obj.get_zones()
        return ZoneSerializer(zone_queryset, many=True).data

    def get_lead_zone(self, obj):
        zone_queryset = obj.get_lead_zone()
        return ZoneSerializer(zone_queryset).data

    class Meta:
        model = Bento
        fields = ('lead_zone', 'zones', )

I need the data coming out of the serializer to be nested JSON (the ZoneSerializer contains similar SerializerMethodFields of its own, alongside normal model fields) but instead it comes out as an OrderedDict.

Is there a way to configure the serializer such that BentoSerializer(obj).data return nested JSON, should I be recursively turning the OrderedDicts into dicts, or is there some other serializer method I don't know about for getting non-ordered data?

Many thanks!

PS: Example data currently coming from a factory object sent through the serializer:

[OrderedDict([('order', 1), ('columns', [OrderedDict([('order', 1), ('blocks', [OrderedDict([('order', 1), ('block_type', 'Text'), ('blockcontent', 'Cum inventore sed fugit aliquam doloribus. Alias exercitationem odit asperiores rerum qui aperiam cum fugit.'), ('heading', 'Adipisci possimus dolore assumenda sapiente velit amet odio doloremque.')])]), ('sticky', False), ('weight', 12), ('is_slideshow', False)])]), ('has_background_image', False), ('is_lead', False), ('is_slideshow', False), ('background_image', None), ('background_treatment', 'dark')])]
<class 'rest_framework.utils.serializer_helpers.ReturnList'>
{'lead_zone': {'background_image': None,
               'background_treatment': 'dark',
               'columns': [OrderedDict([('order', 1), ('blocks', [OrderedDict([('order', 1), ('block_type', 'Image'), ('blockcontent', '/media/media/2018/06/22/adatestfile_SfZMYPw.png'), ('heading', 'Reprehenderit officiis aliquid inventore enim quibusdam inventore beatae.')])]), ('sticky', False), ('weight', 12), ('is_slideshow', False)])],
               'has_background_image': False,
               'is_lead': True,
               'is_slideshow': False,
               'order': 1},
 'zones': [OrderedDict([('order', 1), ('columns', [OrderedDict([('order', 1), ('blocks', [OrderedDict([('order', 1), ('block_type', 'Text'), ('blockcontent', 'Cum inventore sed fugit aliquam doloribus. Alias exercitationem odit asperiores rerum qui aperiam cum fugit.'), ('heading', 'Adipisci possimus dolore assumenda sapiente velit amet odio doloremque.')])]), ('sticky', False), ('weight', 12), ('is_slideshow', False)])]), ('has_background_image', False), ('is_lead', False), ('is_slideshow', False), ('background_image', None), ('background_treatment', 'dark')])]}
like image 554
Robert Townley Avatar asked Jun 22 '18 19:06

Robert Townley


1 Answers

class/models are serialized into an OrderedDict type to be consumed by the Response method through this import from rest_framework.response import Response in combination with Django Rest Framework Views. It will typically return the data in a content-type that is either assigned at runtime through the DEFAULT_RENDERER_CLASSES project setting, assigned through a serializer property that is passed on initialization, or will be determined by the Accept header sent during the request. Details can be found in the DRF documentation at http://www.django-rest-framework.org/api-guide/renderers/.

Thus, calling Response(BentoSerializer(obj).data) should return JSON formatted data to the client when passed through a view such as APIView, or a route on a ModelViewSet.

However, you can also just import json and call json.dumps(BentoSerializer(obj).data) and the desired JSON will be output. I tested this with my own serializers in the django shell.

EDIT

Note that you cannot simply dump a datetime... You will likely end up having to write a patch to convert datetime and date objects to the format you expect.

eg.

def json_dt_patch(o):
    import datetime
    from decimal import Decimal

    if isinstance(o, datetime.date) or isinstance(o, datetime.datetime):
        return o.strftime("%Y/%m/%d %H:%M:%S")
    elif isinstance(o, Decimal):
        return str(o)
    return o

import json

json.dumps(data, default=json_dt_patch)
like image 73
Nicholas Claude LeBlanc Avatar answered Sep 20 '22 03:09

Nicholas Claude LeBlanc