Changes between Initial Version and Version 1 of Ticket #31991, comment 4


Ignore:
Timestamp:
Aug 6, 2021, 2:00:52 AM (3 years ago)
Author:
Ruben Nielsen

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #31991, comment 4

    initial v1  
    1515
    1616Or another path. How do I set a custom decoder for all my JSONFields? Earlier, everything was simply `json.loads` by default, so that was all good. I know how to make a custom field with a custom decoder, sure, but how would I then tie that into `queryset.raw()`?
     17
     18
     19--- Edit:
     20
     21Commenting out that line that I am unhappy about will solve my `raw()` problems, but will cause problems elsewhere. My current desperate solution, which seems to solve my issues is to monkey patch the things I didn't want:
     22
     231. Don't disable the psycopg2 defaults
     242. In the SQLCompiler, when doing conversions, if the conversion is for JSON, assume that psycopg2 has already handled it and skip it
     25
     26It is very hamfisted, but it works. Any pointers for more sensible solution would be much appreciated :)
     27
     28
     29{{{
     30@async_unsafe
     31def monkey_get_new_connection(self, conn_params):
     32    connection = Database.connect(**conn_params)
     33
     34    # self.isolation_level must be set:
     35    # - after connecting to the database in order to obtain the database's
     36    #   default when no value is explicitly specified in options.
     37    # - before calling _set_autocommit() because if autocommit is on, that
     38    #   will set connection.isolation_level to ISOLATION_LEVEL_AUTOCOMMIT.
     39    options = self.settings_dict['OPTIONS']
     40    try:
     41        self.isolation_level = options['isolation_level']
     42    except KeyError:
     43        self.isolation_level = connection.isolation_level
     44    else:
     45        # Set the isolation level to the value from OPTIONS.
     46        if self.isolation_level != connection.isolation_level:
     47            connection.set_session(isolation_level=self.isolation_level)
     48
     49    # --- Monkey: This line was introduces by Django team, which changes the
     50    # --- defaults of psycopg2. We need to not do that.
     51    # --- (https://code.djangoproject.com/ticket/31991)
     52    # Register dummy loads() to avoid a round trip from psycopg2's decode
     53    # to json.dumps() to json.loads(), when using a custom decoder in
     54    # JSONField.
     55    #psycopg2.extras.register_default_jsonb(conn_or_curs=connection, loads=lambda x: x)
     56    # ---
     57    return connection
     58DatabaseWrapper.get_new_connection = monkey_get_new_connection
     59
     60
     61def monkey_apply_converters(self, rows, converters):
     62    connection = self.connection
     63    converters = list(converters.items())
     64    for row in map(list, rows):
     65        for pos, (convs, expression) in converters:
     66            value = row[pos]
     67            for converter in convs:
     68                # --- Monkey: `monkey_get_new_connection` now lets psycopg2 deal
     69                # --- with JSON conversion for us, so when we arrive at this point
     70                # --- it'll already be a dict. So if the converter is for JSON,
     71                # --- We can safely skip it
     72                module_name = str(inspect.getmodule(converter))
     73                is_json = 'django/db/models/fields/json.py' in module_name
     74                is_array = 'django/contrib/postgres/fields/array.py' in module_name
     75                if is_json or is_array:
     76                    continue
     77                # ---
     78                value = converter(value, expression, connection)
     79            row[pos] = value
     80        yield row
     81SQLCompiler.apply_converters = monkey_apply_converters
     82}}}
Back to Top