Make from_db_value compatible with postgres >= 9.4

#32 Declined
Repository
Deleted repository
Branch
default (00b05528d070)
Repository
django-jsonfield
Branch
default
Author
  1. Anonymous
Reviewers
Description

When getting a value from the database, if using postgresql's JSONB, the value is already an object. This causes from_db_value as written to fail, since it assumes the database value is a string that can be a JSON-deserializable string. This causes the json.loads to fail, since the value returned from the database is the already-deserialized object.

This adds a check if it the database value a string before attempting to deserialize it - if it's not a string, return the value as-is.

Tested on postgres 9.4.5

Comments (10)

  1. Matthew Schinckel repo owner

    Thanks for the contribution.

    I believe it is possible to have a JSON string stored in PG 9.4+, i.e. '"Foo"'.

    This doesn't address that: neither does the original code. ;)

    I think this stuff needs more thought (and some test cases).

    Also, is there any reason you can't use django.contrib.postgres?

  2. Raphaël Hertzog

    I wonder if we shouldn't just change the code to not create "jsonb" fields... Right now I have a problem where a migration converting a TextField into a JSONField doesn't work and I assume it's because jsonfield wants a "jsonb" field as soon as we have a version of postgres which support that type.

    As you explain, someone really interested in what the "jsonb" type brings would use django.contrib.postgres.

  3. Raphaël Hertzog

    Otherwise we should look if one of the other parameters (expression, context) let us discover the underlying type and use that information to decide. I also wonder if the version of pyscopg2 plays a role here. I guess that's the reason why we get something else than a string in the first place...

            1. Raphaël Hertzog

              Except that django-jsonfield will never create a fielt of "json" type. It's either "jsonb" or "text":

                  def db_type(self, connection):
                      if connection.vendor == 'postgresql':
                          # Only do jsonb if in pg 9.4+
                          if connection.pg_version >= 90400:
                              return 'jsonb'
                          return 'text'
                      if connection.vendor == 'mysql':
                          return 'longtext'
                      if connection.vendor == 'oracle':
                          return 'long'
                      return 'text'
              

              So that trick is only needed if you created the field with a very old version jsonfield when the "jsonb" type did not exist in Postgres yet. I would suggest to solve your problem by upgrading your field to jsonb...