laymonage / django-jsonfield-backport

Backport of the cross-DB JSONField model and form fields from Django 3.1.
https://pypi.org/project/django-jsonfield-backport
BSD 3-Clause "New" or "Revised" License
42 stars 6 forks source link

JSONField does not decode the serialized JSON and returns a string #29

Open Mapiarz opened 3 years ago

Mapiarz commented 3 years ago

Hi!

First of all, thank you for this package. It is very much needed as it brings some sanity into django - json world.

I'm running Django 2.2 LTS with psql 12. I've added a JSONField to my model, all works good. I can persist some data and make queries. Nonetheless, when I retrieve an object with the JSONField, the field is an ordinary string and not a Python dict as one would expect:

>>> e.extra_headers = {'foo': 123}
>>> e.save()
>>> e.refresh_from_db()
>>> e.extra_headers
'{"foo": 123}'
>>> type(e.extra_headers)
<class 'str'>

On this line: https://github.com/laymonage/django-jsonfield-backport/blob/2072fb39b6681f2bf8741e033702920b59238941/src/django_jsonfield_backport/models.py#L111 The connection.features.has_native_json_field is True and self.decoder is None.

If I explicitly set decoder to json.JSONDecoder on my field (even though Django docs mention this is the default anyway), everything seems to work as intended.

Any ideas what's wrong and what would be the proper solution to this?

Thanks!

laymonage commented 3 years ago

Hi @Mapiarz, thanks for raising the issue.

That is weird. If you're not setting a custom decoder (that is, self.decoder is None), we let psycopg2 decode the data, which also uses the json.JSONDecoder by default. If self.decoder is not None, we cast the value to text to prevent psycopg2 from decoding the data, then we decode the data ourselves.

Do you happen to have disabled the automatic parsing by using psycopg2.extras.register_default_jsonb somewhere in your code?

DanielSchaffer commented 2 years ago

For anyone else finding this - I was running into this on MySQL because we have a custom features class for our setup. I'd added the attributes manually, but that didn't worked - what did work was adding the MySQLFeatures as a super class to the features class we are using.