| 17 | |
| 18 | |
| 19 | --- Edit: |
| 20 | |
| 21 | Commenting 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 | |
| 23 | 1. Don't disable the psycopg2 defaults |
| 24 | 2. In the SQLCompiler, when doing conversions, if the conversion is for JSON, assume that psycopg2 has already handled it and skip it |
| 25 | |
| 26 | It is very hamfisted, but it works. Any pointers for more sensible solution would be much appreciated :) |
| 27 | |
| 28 | |
| 29 | {{{ |
| 30 | @async_unsafe |
| 31 | def 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 |
| 58 | DatabaseWrapper.get_new_connection = monkey_get_new_connection |
| 59 | |
| 60 | |
| 61 | def 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 |
| 81 | SQLCompiler.apply_converters = monkey_apply_converters |
| 82 | }}} |