3

I am having a hard time preserving the order of keys in a JSON object stored in a Django JSONField. I have tried using a custom encoder and decoder as per the docs, but the JSON object keeps re-ordeing itself:

>>>from models import MyModel
>>>my_dict = {"c": 3, "b": 2, "a": 1}
>>>my_model = MyModel()
>>>my_model.my_field = my_dict
>>>my_model.save()
>>>my_model.refresh_from_db()
>>>my_model.my_field
OrderedDict([('a',1), ('b', 2), ('c', 3)])

I would have expected it to return OrderedDict([('c',3), ('b', 2), ('a', 1)]).

Here is what I've tried so far:

models.py:

import collections
import json
from django.db import models

class OrderedEncoder(json.JSONEncoder):
  def encode(self, o):
    if isinstance(o, collections.OrderedDict):
      encoded_objs = [
        f"{self.encode(k)}: {self.encode(v)}"
        for k, v in o.items()
      ]
      return f"{{{','.join(encoded_objs)}}}"
   else:
     return super().encode(o)

class OrderedDecoder(json.JSONDecoder):
    def __init__(self, *args, **kwargs):
        default_kwargs = {
            "object_pairs_hook": collections.OrderedDict
        }
        default_kwargs.update(kwargs)
        super().__init__(*args, **default_kwargs)

class MyModel(models.Model):
  my_field = models.JSONField(encoder=OrderedEncoder, decoder=OrderedDecoder)

Any ideas?

trubliphone
  • 3,679
  • 2
  • 37
  • 56
  • You know that dict keys don't have stable ordering right? So start with ">>>my_dict = {"c": 3, "b": 2, "a": 1}" and that gets saved into undefined order. And second, why does your application rely on key order? If that's the case, then you should sort them retrieval. –  Dec 10 '20 at 11:10
  • This is not possible, at least in MySQL. Databases arranging the data in *their own order* (ascending, maybe ?). This is expected behavior to *optimize* the querying inside the JSON – JPG Dec 10 '20 at 11:12
  • The reordering is often done by the database. If you are using PostgreSQL, you can use the `json` data type which does not perform the reordering rather than the `jsonb` data type which *does* perform the reordering. This data type is also used by Django's `JSONField` in case of a PostgreSQL backend. I am unaware of any way to adjust `JSONField` to use `json` instead. One of the work-arounds would probably be to define a custom Django field type. Or to use e.g. a `TextField`. Related answer: https://stackoverflow.com/a/66829995/1018447 – Peter Bašista Jan 12 '22 at 19:25

0 Answers0