diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 8bdf6eed3cf..ce3ea04d980 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -614,9 +614,6 @@ class BaseDatabaseFeatures(object): # Can the backend introspect an BinaryField, instead of an TextField? can_introspect_binary_field = True - # What is the type returned when the backend introspect a BooleanField? - introspected_boolean_field_type = 'BooleanField' - # Can the backend introspect an DecimalField, instead of an FloatField? can_introspect_decimal_field = True @@ -715,6 +712,23 @@ class BaseDatabaseFeatures(object): except NotImplementedError: return False + def introspected_boolean_field_type(self, field=None, created_separately=False): + """ + What is the type returned when the backend introspects a BooleanField? + The optional arguments may be used to give further details of the field to be + introspected; in particular, they are provided by Django's test suite: + field -- the field definition + created_separately -- True if the field was added via a SchemaEditor's AddField, + False if the field was created with the model + + Note that return value from this function is compared by tests against actual + introspection results; it should provide expectations, not run an introspection + itself. + """ + if self.can_introspect_null and field and field.null: + return 'NullBooleanField' + return 'BooleanField' + class BaseDatabaseOperations(object): """ diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 0bd9c16e56a..d2ba75f664e 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -178,7 +178,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_regex_backreferencing = False supports_date_lookup_using_string = False can_introspect_binary_field = False - introspected_boolean_field_type = 'IntegerField' can_introspect_small_integer_field = True supports_timezones = False requires_explicit_null_ordering_when_grouping = True @@ -225,6 +224,9 @@ class DatabaseFeatures(BaseDatabaseFeatures): cursor.execute("SELECT 1 FROM mysql.time_zone LIMIT 1") return cursor.fetchone() is not None + def introspected_boolean_field_type(self, *args): + return 'IntegerField' + class DatabaseOperations(BaseDatabaseOperations): compiler_module = "django.db.backends.mysql.compiler" diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index c9a59d39704..b41386d4f04 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -125,19 +125,20 @@ class DatabaseFeatures(BaseDatabaseFeatures): # select for update with limit can be achieved on Oracle, but not with the current backend. supports_select_for_update_with_limit = False - @cached_property - def introspected_boolean_field_type(self): + def introspected_boolean_field_type(self, field=None, created_separately=False): """ Some versions of Oracle -- we've seen this on 11.2.0.1 and suspect it goes back -- have a weird bug where, when an integer column is - defined with a default, its precision is later reported on introspection - as 0, regardless of the real precision. For Django introspection, this - means that such columns are reported as IntegerField even if they are - really BigIntegerField or BooleanField. + added to an existing table with a default, its precision is later + reported on introspection as 0, regardless of the real precision. + For Django introspection, this means that such columns are reported + as IntegerField even if they are really BigIntegerField or BooleanField. The bug is solved in Oracle 11.2.0.2 and up. """ - return 'IntegerField' if self.connection.oracle_full_version < '11.2.0.2' else 'BooleanField' + if self.connection.oracle_full_version < '11.2.0.2' and field and field.has_default() and created_separately: + return 'IntegerField' + return super(DatabaseFeatures, self).introspected_boolean_field_type(field, created_separately) class DatabaseOperations(BaseDatabaseOperations): diff --git a/tests/inspectdb/tests.py b/tests/inspectdb/tests.py index 8b187191ae7..708bae4fb85 100644 --- a/tests/inspectdb/tests.py +++ b/tests/inspectdb/tests.py @@ -9,6 +9,8 @@ from django.db import connection from django.test import TestCase, skipUnlessDBFeature from django.utils.six import PY3, StringIO +from .models import ColumnTypes + class InspectDBTestCase(TestCase): @@ -87,19 +89,18 @@ class InspectDBTestCase(TestCase): else: assertFieldType('big_int_field', "models.IntegerField()") - if connection.features.introspected_boolean_field_type == 'BooleanField': - assertFieldType('bool_field', "models.BooleanField()") - if connection.features.can_introspect_null: - assertFieldType('null_bool_field', "models.NullBooleanField()") - else: - assertFieldType('null_bool_field', "models.BooleanField()") + bool_field = ColumnTypes._meta.get_field('bool_field') + bool_field_type = connection.features.introspected_boolean_field_type(bool_field) + assertFieldType('bool_field', "models.{}()".format(bool_field_type)) + null_bool_field = ColumnTypes._meta.get_field('null_bool_field') + null_bool_field_type = connection.features.introspected_boolean_field_type(null_bool_field) + if 'BooleanField' in null_bool_field_type: + assertFieldType('null_bool_field', "models.{}()".format(null_bool_field_type)) else: - field_type = connection.features.introspected_boolean_field_type - assertFieldType('bool_field', "models.{}()".format(field_type)) if connection.features.can_introspect_null: - assertFieldType('null_bool_field', "models.{}(blank=True, null=True)".format(field_type)) + assertFieldType('null_bool_field', "models.{}(blank=True, null=True)".format(null_bool_field_type)) else: - assertFieldType('null_bool_field', "models.{}()".format(field_type)) + assertFieldType('null_bool_field', "models.{}()".format(null_bool_field_type)) if connection.features.can_introspect_decimal_field: assertFieldType('decimal_field', "models.DecimalField(max_digits=6, decimal_places=1)") diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 82377041dcb..88290338a1c 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -314,7 +314,7 @@ class SchemaTests(TransactionTestCase): columns = self.column_classes(Author) # BooleanField are stored as TINYINT(1) on MySQL. field_type = columns['awesome'][0] - self.assertEqual(field_type, connection.features.introspected_boolean_field_type) + self.assertEqual(field_type, connection.features.introspected_boolean_field_type(new_field, created_separately=True)) def test_add_field_default_transform(self): """