Browse Source

Moved DatabaseCreation.data_types properties to DatabaseWrapper.

refs #22340.
Tim Graham 10 years ago
parent
commit
93d73dac91

+ 6 - 0
django/db/backends/__init__.py

@@ -39,6 +39,12 @@ class BaseDatabaseWrapper(object):
     """
     Represents a database connection.
     """
+    # Mapping of Field objects to their column types.
+    data_types = {}
+    # Mapping of Field objects to their SQL suffix such as AUTOINCREMENT.
+    data_types_suffix = {}
+    # Mapping of Field objects to their SQL for CHECK constraints.
+    data_type_check_constraints = {}
     ops = None
     vendor = 'unknown'
     SchemaEditorClass = None

+ 0 - 4
django/db/backends/creation.py

@@ -26,10 +26,6 @@ class BaseDatabaseCreation(object):
     Fields, the SQL used to create and destroy tables, and the creation and
     destruction of test databases.
     """
-    data_types = {}
-    data_types_suffix = {}
-    data_type_check_constraints = {}
-
     def __init__(self, connection):
         self.connection = connection
 

+ 39 - 0
django/db/backends/mysql/base.py

@@ -415,6 +415,45 @@ class DatabaseOperations(BaseDatabaseOperations):
 
 class DatabaseWrapper(BaseDatabaseWrapper):
     vendor = 'mysql'
+    # This dictionary maps Field objects to their associated MySQL column
+    # types, as strings. Column-type strings can contain format strings; they'll
+    # be interpolated against the values of Field.__dict__ before being output.
+    # If a column type is set to None, it won't be included in the output.
+    _data_types = {
+        'AutoField': 'integer AUTO_INCREMENT',
+        'BinaryField': 'longblob',
+        'BooleanField': 'bool',
+        'CharField': 'varchar(%(max_length)s)',
+        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
+        'DateField': 'date',
+        'DateTimeField': 'datetime',
+        'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
+        'DurationField': 'bigint',
+        'FileField': 'varchar(%(max_length)s)',
+        'FilePathField': 'varchar(%(max_length)s)',
+        'FloatField': 'double precision',
+        'IntegerField': 'integer',
+        'BigIntegerField': 'bigint',
+        'IPAddressField': 'char(15)',
+        'GenericIPAddressField': 'char(39)',
+        'NullBooleanField': 'bool',
+        'OneToOneField': 'integer',
+        'PositiveIntegerField': 'integer UNSIGNED',
+        'PositiveSmallIntegerField': 'smallint UNSIGNED',
+        'SlugField': 'varchar(%(max_length)s)',
+        'SmallIntegerField': 'smallint',
+        'TextField': 'longtext',
+        'TimeField': 'time',
+        'UUIDField': 'char(32)',
+    }
+
+    @cached_property
+    def data_types(self):
+        if self.features.supports_microsecond_precision:
+            return dict(self._data_types, DateTimeField='datetime(6)', TimeField='time(6)')
+        else:
+            return self._data_types
+
     operators = {
         'exact': '= %s',
         'iexact': 'LIKE %s',

+ 0 - 39
django/db/backends/mysql/creation.py

@@ -1,46 +1,7 @@
 from django.db.backends.creation import BaseDatabaseCreation
-from django.utils.functional import cached_property
 
 
 class DatabaseCreation(BaseDatabaseCreation):
-    # This dictionary maps Field objects to their associated MySQL column
-    # types, as strings. Column-type strings can contain format strings; they'll
-    # be interpolated against the values of Field.__dict__ before being output.
-    # If a column type is set to None, it won't be included in the output.
-    _data_types = {
-        'AutoField': 'integer AUTO_INCREMENT',
-        'BinaryField': 'longblob',
-        'BooleanField': 'bool',
-        'CharField': 'varchar(%(max_length)s)',
-        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
-        'DateField': 'date',
-        'DateTimeField': 'datetime',
-        'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
-        'DurationField': 'bigint',
-        'FileField': 'varchar(%(max_length)s)',
-        'FilePathField': 'varchar(%(max_length)s)',
-        'FloatField': 'double precision',
-        'IntegerField': 'integer',
-        'BigIntegerField': 'bigint',
-        'IPAddressField': 'char(15)',
-        'GenericIPAddressField': 'char(39)',
-        'NullBooleanField': 'bool',
-        'OneToOneField': 'integer',
-        'PositiveIntegerField': 'integer UNSIGNED',
-        'PositiveSmallIntegerField': 'smallint UNSIGNED',
-        'SlugField': 'varchar(%(max_length)s)',
-        'SmallIntegerField': 'smallint',
-        'TextField': 'longtext',
-        'TimeField': 'time',
-        'UUIDField': 'char(32)',
-    }
-
-    @cached_property
-    def data_types(self):
-        if self.connection.features.supports_microsecond_precision:
-            return dict(self._data_types, DateTimeField='datetime(6)', TimeField='time(6)')
-        else:
-            return self._data_types
 
     def sql_table_creation_suffix(self):
         suffix = []

+ 42 - 0
django/db/backends/oracle/base.py

@@ -575,6 +575,48 @@ class _UninitializedOperatorsDescriptor(object):
 
 class DatabaseWrapper(BaseDatabaseWrapper):
     vendor = 'oracle'
+    # This dictionary maps Field objects to their associated Oracle column
+    # types, as strings. Column-type strings can contain format strings; they'll
+    # be interpolated against the values of Field.__dict__ before being output.
+    # If a column type is set to None, it won't be included in the output.
+    #
+    # Any format strings starting with "qn_" are quoted before being used in the
+    # output (the "qn_" prefix is stripped before the lookup is performed.
+    data_types = {
+        'AutoField': 'NUMBER(11)',
+        'BinaryField': 'BLOB',
+        'BooleanField': 'NUMBER(1)',
+        'CharField': 'NVARCHAR2(%(max_length)s)',
+        'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)',
+        'DateField': 'DATE',
+        'DateTimeField': 'TIMESTAMP',
+        'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)',
+        'DurationField': 'INTERVAL DAY(9) TO SECOND(6)',
+        'FileField': 'NVARCHAR2(%(max_length)s)',
+        'FilePathField': 'NVARCHAR2(%(max_length)s)',
+        'FloatField': 'DOUBLE PRECISION',
+        'IntegerField': 'NUMBER(11)',
+        'BigIntegerField': 'NUMBER(19)',
+        'IPAddressField': 'VARCHAR2(15)',
+        'GenericIPAddressField': 'VARCHAR2(39)',
+        'NullBooleanField': 'NUMBER(1)',
+        'OneToOneField': 'NUMBER(11)',
+        'PositiveIntegerField': 'NUMBER(11)',
+        'PositiveSmallIntegerField': 'NUMBER(11)',
+        'SlugField': 'NVARCHAR2(%(max_length)s)',
+        'SmallIntegerField': 'NUMBER(11)',
+        'TextField': 'NCLOB',
+        'TimeField': 'TIMESTAMP',
+        'URLField': 'VARCHAR2(%(max_length)s)',
+        'UUIDField': 'VARCHAR2(32)',
+    }
+    data_type_check_constraints = {
+        'BooleanField': '%(qn_column)s IN (0,1)',
+        'NullBooleanField': '(%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL)',
+        'PositiveIntegerField': '%(qn_column)s >= 0',
+        'PositiveSmallIntegerField': '%(qn_column)s >= 0',
+    }
+
     operators = _UninitializedOperatorsDescriptor()
 
     _standard_operators = {

+ 0 - 43
django/db/backends/oracle/creation.py

@@ -12,49 +12,6 @@ PASSWORD = 'Im_a_lumberjack'
 
 
 class DatabaseCreation(BaseDatabaseCreation):
-    # This dictionary maps Field objects to their associated Oracle column
-    # types, as strings. Column-type strings can contain format strings; they'll
-    # be interpolated against the values of Field.__dict__ before being output.
-    # If a column type is set to None, it won't be included in the output.
-    #
-    # Any format strings starting with "qn_" are quoted before being used in the
-    # output (the "qn_" prefix is stripped before the lookup is performed.
-
-    data_types = {
-        'AutoField': 'NUMBER(11)',
-        'BinaryField': 'BLOB',
-        'BooleanField': 'NUMBER(1)',
-        'CharField': 'NVARCHAR2(%(max_length)s)',
-        'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)',
-        'DateField': 'DATE',
-        'DateTimeField': 'TIMESTAMP',
-        'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)',
-        'DurationField': 'INTERVAL DAY(9) TO SECOND(6)',
-        'FileField': 'NVARCHAR2(%(max_length)s)',
-        'FilePathField': 'NVARCHAR2(%(max_length)s)',
-        'FloatField': 'DOUBLE PRECISION',
-        'IntegerField': 'NUMBER(11)',
-        'BigIntegerField': 'NUMBER(19)',
-        'IPAddressField': 'VARCHAR2(15)',
-        'GenericIPAddressField': 'VARCHAR2(39)',
-        'NullBooleanField': 'NUMBER(1)',
-        'OneToOneField': 'NUMBER(11)',
-        'PositiveIntegerField': 'NUMBER(11)',
-        'PositiveSmallIntegerField': 'NUMBER(11)',
-        'SlugField': 'NVARCHAR2(%(max_length)s)',
-        'SmallIntegerField': 'NUMBER(11)',
-        'TextField': 'NCLOB',
-        'TimeField': 'TIMESTAMP',
-        'URLField': 'VARCHAR2(%(max_length)s)',
-        'UUIDField': 'VARCHAR2(32)',
-    }
-
-    data_type_check_constraints = {
-        'BooleanField': '%(qn_column)s IN (0,1)',
-        'NullBooleanField': '(%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL)',
-        'PositiveIntegerField': '%(qn_column)s >= 0',
-        'PositiveSmallIntegerField': '%(qn_column)s >= 0',
-    }
 
     def _create_test_db(self, verbosity=1, autoclobber=False, keepdb=False):
         parameters = self._get_test_db_params()

+ 35 - 0
django/db/backends/postgresql_psycopg2/base.py

@@ -71,6 +71,41 @@ class DatabaseFeatures(BaseDatabaseFeatures):
 
 class DatabaseWrapper(BaseDatabaseWrapper):
     vendor = 'postgresql'
+    # This dictionary maps Field objects to their associated PostgreSQL column
+    # types, as strings. Column-type strings can contain format strings; they'll
+    # be interpolated against the values of Field.__dict__ before being output.
+    # If a column type is set to None, it won't be included in the output.
+    data_types = {
+        'AutoField': 'serial',
+        'BinaryField': 'bytea',
+        'BooleanField': 'boolean',
+        'CharField': 'varchar(%(max_length)s)',
+        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
+        'DateField': 'date',
+        'DateTimeField': 'timestamp with time zone',
+        'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
+        'DurationField': 'interval',
+        'FileField': 'varchar(%(max_length)s)',
+        'FilePathField': 'varchar(%(max_length)s)',
+        'FloatField': 'double precision',
+        'IntegerField': 'integer',
+        'BigIntegerField': 'bigint',
+        'IPAddressField': 'inet',
+        'GenericIPAddressField': 'inet',
+        'NullBooleanField': 'boolean',
+        'OneToOneField': 'integer',
+        'PositiveIntegerField': 'integer',
+        'PositiveSmallIntegerField': 'smallint',
+        'SlugField': 'varchar(%(max_length)s)',
+        'SmallIntegerField': 'smallint',
+        'TextField': 'text',
+        'TimeField': 'time',
+        'UUIDField': 'uuid',
+    }
+    data_type_check_constraints = {
+        'PositiveIntegerField': '"%(column)s" >= 0',
+        'PositiveSmallIntegerField': '"%(column)s" >= 0',
+    }
     operators = {
         'exact': '= %s',
         'iexact': '= UPPER(%s)',

+ 0 - 36
django/db/backends/postgresql_psycopg2/creation.py

@@ -3,42 +3,6 @@ from django.db.backends.utils import truncate_name
 
 
 class DatabaseCreation(BaseDatabaseCreation):
-    # This dictionary maps Field objects to their associated PostgreSQL column
-    # types, as strings. Column-type strings can contain format strings; they'll
-    # be interpolated against the values of Field.__dict__ before being output.
-    # If a column type is set to None, it won't be included in the output.
-    data_types = {
-        'AutoField': 'serial',
-        'BinaryField': 'bytea',
-        'BooleanField': 'boolean',
-        'CharField': 'varchar(%(max_length)s)',
-        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
-        'DateField': 'date',
-        'DateTimeField': 'timestamp with time zone',
-        'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
-        'DurationField': 'interval',
-        'FileField': 'varchar(%(max_length)s)',
-        'FilePathField': 'varchar(%(max_length)s)',
-        'FloatField': 'double precision',
-        'IntegerField': 'integer',
-        'BigIntegerField': 'bigint',
-        'IPAddressField': 'inet',
-        'GenericIPAddressField': 'inet',
-        'NullBooleanField': 'boolean',
-        'OneToOneField': 'integer',
-        'PositiveIntegerField': 'integer',
-        'PositiveSmallIntegerField': 'smallint',
-        'SlugField': 'varchar(%(max_length)s)',
-        'SmallIntegerField': 'smallint',
-        'TextField': 'text',
-        'TimeField': 'time',
-        'UUIDField': 'uuid',
-    }
-
-    data_type_check_constraints = {
-        'PositiveIntegerField': '"%(column)s" >= 0',
-        'PositiveSmallIntegerField': '"%(column)s" >= 0',
-    }
 
     def sql_table_creation_suffix(self):
         test_settings = self.connection.settings_dict['TEST']

+ 33 - 0
django/db/backends/sqlite3/base.py

@@ -336,6 +336,39 @@ class DatabaseOperations(BaseDatabaseOperations):
 
 class DatabaseWrapper(BaseDatabaseWrapper):
     vendor = 'sqlite'
+    # SQLite doesn't actually support most of these types, but it "does the right
+    # thing" given more verbose field definitions, so leave them as is so that
+    # schema inspection is more useful.
+    data_types = {
+        'AutoField': 'integer',
+        'BinaryField': 'BLOB',
+        'BooleanField': 'bool',
+        'CharField': 'varchar(%(max_length)s)',
+        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
+        'DateField': 'date',
+        'DateTimeField': 'datetime',
+        'DecimalField': 'decimal',
+        'DurationField': 'bigint',
+        'FileField': 'varchar(%(max_length)s)',
+        'FilePathField': 'varchar(%(max_length)s)',
+        'FloatField': 'real',
+        'IntegerField': 'integer',
+        'BigIntegerField': 'bigint',
+        'IPAddressField': 'char(15)',
+        'GenericIPAddressField': 'char(39)',
+        'NullBooleanField': 'bool',
+        'OneToOneField': 'integer',
+        'PositiveIntegerField': 'integer unsigned',
+        'PositiveSmallIntegerField': 'smallint unsigned',
+        'SlugField': 'varchar(%(max_length)s)',
+        'SmallIntegerField': 'smallint',
+        'TextField': 'text',
+        'TimeField': 'time',
+        'UUIDField': 'char(32)',
+    }
+    data_types_suffix = {
+        'AutoField': 'AUTOINCREMENT',
+    }
     # SQLite requires LIKE statements to include an ESCAPE clause if the value
     # being escaped has a percent or underscore in it.
     # See http://www.sqlite.org/lang_expr.html for an explanation.

+ 0 - 33
django/db/backends/sqlite3/creation.py

@@ -7,39 +7,6 @@ from django.utils.six.moves import input
 
 
 class DatabaseCreation(BaseDatabaseCreation):
-    # SQLite doesn't actually support most of these types, but it "does the right
-    # thing" given more verbose field definitions, so leave them as is so that
-    # schema inspection is more useful.
-    data_types = {
-        'AutoField': 'integer',
-        'BinaryField': 'BLOB',
-        'BooleanField': 'bool',
-        'CharField': 'varchar(%(max_length)s)',
-        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
-        'DateField': 'date',
-        'DateTimeField': 'datetime',
-        'DecimalField': 'decimal',
-        'DurationField': 'bigint',
-        'FileField': 'varchar(%(max_length)s)',
-        'FilePathField': 'varchar(%(max_length)s)',
-        'FloatField': 'real',
-        'IntegerField': 'integer',
-        'BigIntegerField': 'bigint',
-        'IPAddressField': 'char(15)',
-        'GenericIPAddressField': 'char(39)',
-        'NullBooleanField': 'bool',
-        'OneToOneField': 'integer',
-        'PositiveIntegerField': 'integer unsigned',
-        'PositiveSmallIntegerField': 'smallint unsigned',
-        'SlugField': 'varchar(%(max_length)s)',
-        'SmallIntegerField': 'smallint',
-        'TextField': 'text',
-        'TimeField': 'time',
-        'UUIDField': 'char(32)',
-    }
-    data_types_suffix = {
-        'AutoField': 'AUTOINCREMENT',
-    }
 
     def sql_for_pending_references(self, model, style, pending_references):
         "SQLite3 doesn't support constraints"

+ 3 - 3
django/db/models/fields/__init__.py

@@ -537,7 +537,7 @@ class Field(RegisterLookupMixin):
         # exactly which wacky database column type you want to use.
         data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
         try:
-            return connection.creation.data_types[self.get_internal_type()] % data
+            return connection.data_types[self.get_internal_type()] % data
         except KeyError:
             return None
 
@@ -550,7 +550,7 @@ class Field(RegisterLookupMixin):
         data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
         type_string = self.db_type(connection)
         try:
-            check_string = connection.creation.data_type_check_constraints[self.get_internal_type()] % data
+            check_string = connection.data_type_check_constraints[self.get_internal_type()] % data
         except KeyError:
             check_string = None
         return {
@@ -559,7 +559,7 @@ class Field(RegisterLookupMixin):
         }
 
     def db_type_suffix(self, connection):
-        return connection.creation.data_types_suffix.get(self.get_internal_type())
+        return connection.data_types_suffix.get(self.get_internal_type())
 
     def get_db_converters(self, connection):
         if hasattr(self, 'from_db_value'):

+ 10 - 0
docs/releases/1.8.txt

@@ -849,6 +849,16 @@ Also private APIs ``django.template.base.compile_string()``,
 ``django.template.loader.find_template()``, and
 ``django.template.loader.get_template_from_string()`` were removed.
 
+Database backend API
+~~~~~~~~~~~~~~~~~~~~
+
+The following changes to the database backend API are documented to assist
+those writing third-party backends in updating their code:
+
+* The ``data_types``, ``data_types_suffix``, and
+  ``data_type_check_constraints`` attributes have moved from the
+  ``DatabaseCreation`` class to ``DatabaseWrapper``.
+
 Miscellaneous
 ~~~~~~~~~~~~~
 

+ 1 - 1
tests/commands_sql/tests.py

@@ -45,7 +45,7 @@ class SQLCommandsTestCase(TestCase):
             'commands_sql_comment', 'commands_sql_book', 'commands_sql_book_comments'
         })
 
-    @unittest.skipUnless('PositiveIntegerField' in connections[DEFAULT_DB_ALIAS].creation.data_type_check_constraints, 'Backend does not have checks.')
+    @unittest.skipUnless('PositiveIntegerField' in connections[DEFAULT_DB_ALIAS].data_type_check_constraints, 'Backend does not have checks.')
     def test_sql_create_check(self):
         """Regression test for #23416 -- Check that db_params['check'] is respected."""
         app_config = apps.get_app_config('commands_sql')