3

I am using Django Rest Framework (along with rest_framework_mongoengine) and overriding to_representation() of my serializer to send back my own custom JSON object. Everything is working fine but the order of JSON fields is messed up. Please tell me how to reorder/restructure the JSON object being sent back. The serializer looks like this:

class PharmacySerializer(DocumentSerializer):
    bank_account = serializers.DictField(child=serializers.CharField(), required=False)

    class Meta:
        model = Pharmacy
        fields = (
            'id', 'name', 'email', 'mobile', 'address_line_1', 'address_line_2',
            'city', 'state', 'zip', 'created_by', 'created_on', 'last_updated_by', 'license_number',
            'bank_account', 'last_updated_on', 'is_email_verified', 'is_mobile_verified', 'is_active')
        read_only_fields = ('created_by', 'created_on', 'last_updated_by', 'last_updated_on', 'is_active')

    def to_representation(self, pharmacy):
        return {
            'id': str(pharmacy.id),
            'name': pharmacy.name,
            'email': pharmacy.email,
            'mobile': pharmacy.mobile,
            'address_line_1': pharmacy.address_line_1,
            'address_line_2': pharmacy.address_line_2,
            'city': pharmacy.city,
            'state': pharmacy.state,
            'zip': pharmacy.zip,
            'created_by': pharmacy.created_by,
            'created_on': pharmacy.created_on,
            'last_updated_by': pharmacy.last_updated_by,
            'license_number': pharmacy.license_number,
            'bank_account': {
                'bank_name': pharmacy.bank_account.bank_name,
                'account_number': pharmacy.bank_account.account_number,
                'account_type': pharmacy.bank_account.account_type
            },
            'last_updated_on': pharmacy.last_updated_on,
            'is_email_verified': pharmacy.is_email_verified,
            'is_mobile_verified': pharmacy.is_mobile_verified,
            'is_active': pharmacy.is_active
        }

And the response that I am getting is:

[
    {
        "city": "City",
        "name": "Some Pharmacy",
        "zip": "778372",
        "mobile": "9880082229",
        "license_number": "SP22",
        "is_mobile_verified": false,
        "is_active": true,
        "created_on": "2016-06-18T19:01:02.901",
        "created_by": null,
        "email": "somepharmacy@gmail.com",
        "state": "State",
        "last_updated_by": null,
        "is_email_verified": false,
        "last_updated_on": "2016-06-18T19:01:02.901",
        "address_line_2": null,
        "id": "57659a6ea3b6561c0f20265e",
        "bank_account": {
            "bank_name": "HSBC",
            "account_type": "Savings",
            "account_number": "663822553"
        },
        "address_line_1": "Address"
    }
]

I want it to be in the same order as the return dict of to_representation()

  • 2
    Have you tried to use a [`OrderedDict`](https://docs.python.org/2/library/collections.html#collections.OrderedDict)? – gdlmx Jun 18 '16 at 19:33
  • It did not work :( – heaVenShaker Jun 18 '16 at 19:51
  • @heaVenShaker Is there any particular reason you are overriding `to_representation()` method? I don't think you need to do that as you are just accessing the attributes on object which DRF will normally return. Without overriding it, you can get the ordered response. – Rahul Gupta Jun 18 '16 at 20:04
  • 1
    @RahulGupta I am overriding to_representation() because by default it cannot process "bank_account" as "bank_account" is an EmbeddedDocument (please note that I am using MongoEngine with DRF) – heaVenShaker Jun 18 '16 at 20:18
  • 1
    @heaVenShaker Did you try [`EmbeddedDocumentSerializer`](https://pythonhosted.org/django-rest-framework-mongoengine/serializers/#embeddeddocumentserializer) for `bank_account` field? – Rahul Gupta Jun 18 '16 at 20:41
  • @heaVenShaker Can you post the models also. – Rahul Gupta Jun 18 '16 at 20:45
  • 1
    @RahulGupta It worked! thanks for your tip! I used EmbeddedDocumentSerializer and it is working perfectly as I wanted! Somehow I had missed EmbeddedDocumentSerializer and was thinking it did not exist. My bad. So the answer for the question asked is still using OrderedList, but for my usage, your suggestion suits better! – heaVenShaker Jun 18 '16 at 21:06
  • 1
    @heaVenShaker Great! Recommended approach would be to use `EmbeddedDocumentSerializer` without the need of overriding `to_representation`. Have updated my ans to include this. – Rahul Gupta Jun 18 '16 at 21:14
  • @RahulGupta I had one more question related to MongoEngine and Django (not DRF): Is settings.py the best place to create connection while using MongoEngine with django? `from mongoengine import connect; connect('DBName', username='DBUser', password='DBPassword')` – heaVenShaker Jun 18 '16 at 21:14
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/115021/discussion-between-heavenshaker-and-rahul-gupta). – heaVenShaker Jun 18 '16 at 21:16

2 Answers2

3

Try to return a OrderedDict in the to_representation:

def to_representation(self, pharmacy):
  return OrderedDict([('id', str(pharmacy.id),), ...])

The dict object in your code is not ordered in its nature. Assumed the framework use json.dump internally, you can keep the order by using a ordered object as suggested here.

Community
  • 1
  • 1
gdlmx
  • 6,479
  • 1
  • 21
  • 39
3

If you want to maintain order of response keys, you can use an OrderedDict. But there is one thing you should remember about OrderedDicts:

OrderedDict maintains order only while insertion of keys. The order gets lost while initializing with kwargs.

From Python docs:

The OrderedDict constructor and update() method both accept keyword arguments, but their order is lost because Python’s function call semantics pass-in keyword arguments using a regular unordered dictionary.

You need to first create an OrderedDict instance and then add keys one by one.

from collections import OrderedDict

def to_representation(self, pharmacy):
    ret = OrderedDict() # initialize on ordereddict

    # insert keys one by one in desired oreder
    ret['id'] = str(pharmacy.id)
    ret['name'] = pharmacy.name
    ret['email'] = pharmacy.email
    ret['mobile'] = pharmacy.mobile
    ret['address_line_1'] = pharmacy.address_line_1
    ret['address_line_2'] = pharmacy.address_line_2
    ret['city'] = pharmacy.city
    ret['state'] = pharmacy.state
    ret['zip'] = pharmacy.zip
    ret['created_by'] = pharmacy.created_by
    ret['created_on'] = pharmacy.created_on
    ret['last_updated_by'] = pharmacy.last_updated_by
    ret['license_number'] = pharmacy.license_number
    ret['bank_account'] = {
        'bank_name' = pharmacy.bank_account.bank_name
        'account_number' = pharmacy.bank_account.account_number
        'account_type' = pharmacy.bank_account.account_type
    }
    ret['last_updated_on'] = pharmacy.last_updated_on
    ret['is_email_verified'] = pharmacy.is_email_verified
    ret['is_mobile_verified'] = pharmacy.is_mobile_verified
    ret['is_active'] = pharmacy.is_active

    return ret

NOTE: Another option(recommended) is to use EmbeddedDocumentSerializer for bank_account field. Then you won't need to override the to_representation() method.

Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126