From 615e32162ff646db3456b90fb4eaaecc33dd3e4e Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Tue, 30 Jun 2020 09:50:15 +0200 Subject: [PATCH] Fixed #31751 -- Fixed database introspection with cx_Oracle 8. --- .../gis/db/backends/oracle/introspection.py | 9 +++- django/db/backends/oracle/base.py | 4 ++ django/db/backends/oracle/introspection.py | 51 +++++++++++++------ docs/releases/3.0.8.txt | 2 + 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/django/contrib/gis/db/backends/oracle/introspection.py b/django/contrib/gis/db/backends/oracle/introspection.py index f97f0e1446..493fe74b46 100644 --- a/django/contrib/gis/db/backends/oracle/introspection.py +++ b/django/contrib/gis/db/backends/oracle/introspection.py @@ -1,14 +1,19 @@ import cx_Oracle from django.db.backends.oracle.introspection import DatabaseIntrospection +from django.utils.functional import cached_property class OracleIntrospection(DatabaseIntrospection): # Associating any OBJECTVAR instances with GeometryField. This won't work # right on Oracle objects that aren't MDSYS.SDO_GEOMETRY, but it is the # only object type supported within Django anyways. - data_types_reverse = DatabaseIntrospection.data_types_reverse.copy() - data_types_reverse[cx_Oracle.OBJECT] = 'GeometryField' + @cached_property + def data_types_reverse(self): + return { + **super().data_types_reverse, + cx_Oracle.OBJECT: 'GeometryField', + } def get_geometry_type(self, table_name, description): with self.connection.cursor() as cursor: diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index e104530228..10fff26fec 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -323,6 +323,10 @@ class DatabaseWrapper(BaseDatabaseWrapper): else: return True + @cached_property + def cx_oracle_version(self): + return tuple(int(x) for x in Database.version.split('.')) + @cached_property def oracle_version(self): with self.temporary_connection(): diff --git a/django/db/backends/oracle/introspection.py b/django/db/backends/oracle/introspection.py index 3fab497b2a..41ffb68afb 100644 --- a/django/db/backends/oracle/introspection.py +++ b/django/db/backends/oracle/introspection.py @@ -6,29 +6,48 @@ from django.db import models from django.db.backends.base.introspection import ( BaseDatabaseIntrospection, FieldInfo as BaseFieldInfo, TableInfo, ) +from django.utils.functional import cached_property FieldInfo = namedtuple('FieldInfo', BaseFieldInfo._fields + ('is_autofield', 'is_json')) class DatabaseIntrospection(BaseDatabaseIntrospection): - # Maps type objects to Django Field types. - data_types_reverse = { - cx_Oracle.BLOB: 'BinaryField', - cx_Oracle.CLOB: 'TextField', - cx_Oracle.DATETIME: 'DateField', - cx_Oracle.FIXED_CHAR: 'CharField', - cx_Oracle.FIXED_NCHAR: 'CharField', - cx_Oracle.INTERVAL: 'DurationField', - cx_Oracle.NATIVE_FLOAT: 'FloatField', - cx_Oracle.NCHAR: 'CharField', - cx_Oracle.NCLOB: 'TextField', - cx_Oracle.NUMBER: 'DecimalField', - cx_Oracle.STRING: 'CharField', - cx_Oracle.TIMESTAMP: 'DateTimeField', - } - cache_bust_counter = 1 + # Maps type objects to Django Field types. + @cached_property + def data_types_reverse(self): + if self.connection.cx_oracle_version < (8,): + return { + cx_Oracle.BLOB: 'BinaryField', + cx_Oracle.CLOB: 'TextField', + cx_Oracle.DATETIME: 'DateField', + cx_Oracle.FIXED_CHAR: 'CharField', + cx_Oracle.FIXED_NCHAR: 'CharField', + cx_Oracle.INTERVAL: 'DurationField', + cx_Oracle.NATIVE_FLOAT: 'FloatField', + cx_Oracle.NCHAR: 'CharField', + cx_Oracle.NCLOB: 'TextField', + cx_Oracle.NUMBER: 'DecimalField', + cx_Oracle.STRING: 'CharField', + cx_Oracle.TIMESTAMP: 'DateTimeField', + } + else: + return { + cx_Oracle.DB_TYPE_DATE: 'DateField', + cx_Oracle.DB_TYPE_BINARY_DOUBLE: 'FloatField', + cx_Oracle.DB_TYPE_BLOB: 'BinaryField', + cx_Oracle.DB_TYPE_CHAR: 'CharField', + cx_Oracle.DB_TYPE_CLOB: 'TextField', + cx_Oracle.DB_TYPE_INTERVAL_DS: 'DurationField', + cx_Oracle.DB_TYPE_NCHAR: 'CharField', + cx_Oracle.DB_TYPE_NCLOB: 'TextField', + cx_Oracle.DB_TYPE_NVARCHAR: 'CharField', + cx_Oracle.DB_TYPE_NUMBER: 'DecimalField', + cx_Oracle.DB_TYPE_TIMESTAMP: 'DateTimeField', + cx_Oracle.DB_TYPE_VARCHAR: 'CharField', + } + def get_field_type(self, data_type, description): if data_type == cx_Oracle.NUMBER: precision, scale = description[4:6] diff --git a/docs/releases/3.0.8.txt b/docs/releases/3.0.8.txt index f23208f9ae..d702d75764 100644 --- a/docs/releases/3.0.8.txt +++ b/docs/releases/3.0.8.txt @@ -22,3 +22,5 @@ Bugfixes * Fixed a regression in Django 3.0.2 that caused a migration crash on PostgreSQL when adding a foreign key to a model with a namespaced ``db_table`` (:ticket:`31735`). + +* Added compatibility for ``cx_Oracle`` 8 (:ticket:`31751`).