I ran into this problem when I tried to store Peewee's model into PostgreSQL JSONField
.
After struggling for a while, here's the general solution.
The key to my solution is going through Python's source code and realizing that the code documentation (described here) already explains how to extend the existing json.dumps
to support other data types.
Suppose you current have a model that contains some fields that are not serializable to JSON and the model that contains the JSON field originally looks like this:
class SomeClass(Model):
json_field = JSONField()
Just define a custom JSONEncoder
like this:
class CustomJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, SomeTypeUnsupportedByJsonDumps):
return < whatever value you want >
return json.JSONEncoder.default(self, obj)
@staticmethod
def json_dumper(obj):
return json.dumps(obj, cls=CustomJsonEncoder)
And then just use it in your JSONField
like below:
class SomeClass(Model):
json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)
The key is the default(self, obj)
method above. For every single ... is not JSON serializable
complaint you receive from Python, just add code to handle the unserializable-to-JSON type (such as Enum
or datetime
)
For example, here's how I support a class inheriting from Enum
:
class TransactionType(Enum):
CURRENT = 1
STACKED = 2
def default(self, obj):
if isinstance(obj, TransactionType):
return obj.value
return json.JSONEncoder.default(self, obj)
Finally, with the code implemented like above, you can just convert any Peewee models to be a JSON-seriazable object like below:
peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)
Though the code above was (somewhat) specific to Peewee, but I think:
- It's applicable to other ORMs (Django, etc) in general
- Also, if you understood how
json.dumps
works, this solution also works with Python (sans ORM) in general too
Any questions, please post in the comments section. Thanks!
import jsons
see answer below - it works perfectly fine.to_dict()
function or something which can be called on the object before it is passed to the module which tries to serialize it.json.dumps
yet all the answers, including with the bounty awarded, involve creating a custom encoder, which dodges the point of the question entirely.default
hook - which is a simple parameter tojson.dumps
- suffices. One answer simply offersjson.dumps(..., default=vars)
. There's also an answer that does work solely by modifying the class: specifically, it must be modified to subtypedict
. Your assessment of the answers is simply off base.