Fixed #31630 -- Replaced introspection features with DatabaseFeatures.introspected_field_types.

This commit is contained in:
Hasan Ramezani 2020-05-27 00:25:45 +02:00 committed by Mariusz Felisiak
parent 55556e51fb
commit e198beadad
9 changed files with 90 additions and 94 deletions

View File

@ -128,38 +128,22 @@ class BaseDatabaseFeatures:
# Can the backend introspect an AutoField, instead of an IntegerField?
can_introspect_autofield = False
# Can the backend introspect a BigIntegerField, instead of an IntegerField?
can_introspect_big_integer_field = True
# Can the backend introspect an BinaryField, instead of an TextField?
can_introspect_binary_field = True
# Can the backend introspect an DecimalField, instead of an FloatField?
can_introspect_decimal_field = True
# Can the backend introspect a DurationField, instead of a BigIntegerField?
can_introspect_duration_field = True
# Can the backend introspect an IPAddressField, instead of an CharField?
can_introspect_ip_address_field = False
# Can the backend introspect a PositiveIntegerField, instead of an IntegerField?
can_introspect_positive_integer_field = False
# Can the backend introspect a SmallIntegerField, instead of an IntegerField?
can_introspect_small_integer_field = False
# Can the backend introspect a TimeField, instead of a DateTimeField?
can_introspect_time_field = True
# Some backends may not be able to differentiate BigAutoField or
# SmallAutoField from other fields such as AutoField.
introspected_big_auto_field_type = 'BigAutoField'
introspected_small_auto_field_type = 'SmallAutoField'
# Some backends may not be able to differentiate BooleanField from other
# fields such as IntegerField.
introspected_boolean_field_type = 'BooleanField'
# Map fields which some backends may not be able to differentiate to the
# field it's introspected as.
introspected_field_types = {
'BigAutoField': 'BigAutoField',
'BigIntegerField': 'BigIntegerField',
'BinaryField': 'BinaryField',
'BooleanField': 'BooleanField',
'DurationField': 'DurationField',
'GenericIPAddressField': 'GenericIPAddressField',
'PositiveBigIntegerField': 'PositiveBigIntegerField',
'PositiveIntegerField': 'PositiveIntegerField',
'PositiveSmallIntegerField': 'PositiveSmallIntegerField',
'SmallAutoField': 'SmallAutoField',
'SmallIntegerField': 'SmallIntegerField',
'TimeField': 'TimeField',
}
# Can the backend introspect the column order (ASC/DESC) for indexes?
supports_index_column_ordering = True

View File

@ -15,11 +15,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_regex_backreferencing = False
supports_date_lookup_using_string = False
can_introspect_autofield = True
can_introspect_binary_field = False
can_introspect_duration_field = False
can_introspect_small_integer_field = True
can_introspect_positive_integer_field = True
introspected_boolean_field_type = 'IntegerField'
supports_index_column_ordering = False
supports_timezones = False
requires_explicit_null_ordering_when_grouping = True
@ -70,6 +65,16 @@ class DatabaseFeatures(BaseDatabaseFeatures):
"Confirm support for introspected foreign keys"
return self._mysql_storage_engine != 'MyISAM'
@cached_property
def introspected_field_types(self):
return {
**super().introspected_field_types,
'BinaryField': 'TextField',
'BooleanField': 'IntegerField',
'DurationField': 'BigIntegerField',
'GenericIPAddressField': 'CharField',
}
@cached_property
def can_return_columns_from_insert(self):
return self.connection.mysql_is_mariadb and self.connection.mysql_version >= (10, 5, 0)

View File

@ -1,5 +1,6 @@
from django.db import InterfaceError
from django.db.backends.base.features import BaseDatabaseFeatures
from django.utils.functional import cached_property
class DatabaseFeatures(BaseDatabaseFeatures):
@ -22,7 +23,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_tablespaces = True
supports_sequence_reset = False
can_introspect_materialized_views = True
can_introspect_time_field = False
atomic_transactions = False
supports_combined_alters = False
nulls_order_largest = True
@ -61,3 +61,15 @@ class DatabaseFeatures(BaseDatabaseFeatures):
allows_multiple_constraints_on_same_fields = False
supports_boolean_expr_in_select_clause = False
supports_primitives_in_json_field = False
@cached_property
def introspected_field_types(self):
return {
**super().introspected_field_types,
'GenericIPAddressField': 'CharField',
'PositiveBigIntegerField': 'BigIntegerField',
'PositiveIntegerField': 'IntegerField',
'PositiveSmallIntegerField': 'IntegerField',
'SmallIntegerField': 'IntegerField',
'TimeField': 'DateTimeField',
}

View File

@ -22,10 +22,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
can_release_savepoints = True
supports_tablespaces = True
supports_transactions = True
can_introspect_autofield = True
can_introspect_ip_address_field = True
can_introspect_materialized_views = True
can_introspect_small_integer_field = True
can_distinct_on_fields = True
can_rollback_ddl = True
supports_combined_alters = True
@ -61,6 +58,15 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_deferrable_unique_constraints = True
has_json_operators = True
@cached_property
def introspected_field_types(self):
return {
**super().introspected_field_types,
'PositiveBigIntegerField': 'BigIntegerField',
'PositiveIntegerField': 'IntegerField',
'PositiveSmallIntegerField': 'SmallIntegerField',
}
@cached_property
def is_postgresql_10(self):
return self.connection.pg_version >= 100000

View File

@ -19,12 +19,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
max_query_params = 999
supports_mixed_date_datetime_comparisons = False
can_introspect_autofield = True
can_introspect_decimal_field = False
can_introspect_duration_field = False
can_introspect_positive_integer_field = True
can_introspect_small_integer_field = True
introspected_big_auto_field_type = 'AutoField'
introspected_small_auto_field_type = 'AutoField'
supports_transactions = True
atomic_transactions = False
can_rollback_ddl = True
@ -51,6 +45,16 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0)
order_by_nulls_first = True
@cached_property
def introspected_field_types(self):
return{
**super().introspected_field_types,
'BigAutoField': 'AutoField',
'DurationField': 'BigIntegerField',
'GenericIPAddressField': 'CharField',
'SmallAutoField': 'AutoField',
}
@cached_property
def supports_json_field(self):
try:

View File

@ -247,7 +247,20 @@ Database backend API
This section describes changes that may be needed in third-party database
backends.
* ...
* The new ``DatabaseFeatures.introspected_field_types`` property replaces these
features:
* ``can_introspect_big_integer_field``
* ``can_introspect_binary_field``
* ``can_introspect_decimal_field``
* ``can_introspect_duration_field``
* ``can_introspect_ip_address_field``
* ``can_introspect_positive_integer_field``
* ``can_introspect_small_integer_field``
* ``can_introspect_time_field``
* ``introspected_big_auto_field_type``
* ``introspected_small_auto_field_type``
* ``introspected_boolean_field_type``
:mod:`django.contrib.gis`
-------------------------

View File

@ -60,6 +60,7 @@ class InspectDBTestCase(TestCase):
def test_field_types(self):
"""Test introspection of various Django field types"""
assertFieldType = self.make_field_type_asserter()
introspected_field_types = connection.features.introspected_field_types
# Inspecting Oracle DB doesn't produce correct results (#19884):
# - it reports fields as blank=True when they aren't.
@ -74,12 +75,11 @@ class InspectDBTestCase(TestCase):
assertFieldType('url_field', "models.CharField(max_length=200)")
assertFieldType('date_field', "models.DateField()")
assertFieldType('date_time_field', "models.DateTimeField()")
if connection.features.can_introspect_ip_address_field:
if introspected_field_types['GenericIPAddressField'] == 'GenericIPAddressField':
assertFieldType('gen_ip_address_field', "models.GenericIPAddressField()")
elif not connection.features.interprets_empty_strings_as_nulls:
assertFieldType('gen_ip_address_field', "models.CharField(max_length=39)")
if connection.features.can_introspect_time_field:
assertFieldType('time_field', "models.TimeField()")
assertFieldType('time_field', 'models.%s()' % introspected_field_types['TimeField'])
if connection.features.has_native_uuid_field:
assertFieldType('uuid_field', "models.UUIDField()")
elif not connection.features.interprets_empty_strings_as_nulls:
@ -97,20 +97,18 @@ class InspectDBTestCase(TestCase):
def test_number_field_types(self):
"""Test introspection of various Django field types"""
assertFieldType = self.make_field_type_asserter()
introspected_field_types = connection.features.introspected_field_types
if not connection.features.can_introspect_autofield:
assertFieldType('id', "models.IntegerField(primary_key=True) # AutoField?")
if connection.features.can_introspect_big_integer_field:
assertFieldType('big_int_field', "models.BigIntegerField()")
else:
assertFieldType('big_int_field', "models.IntegerField()")
assertFieldType('big_int_field', 'models.%s()' % introspected_field_types['BigIntegerField'])
bool_field_type = connection.features.introspected_boolean_field_type
bool_field_type = introspected_field_types['BooleanField']
assertFieldType('bool_field', "models.{}()".format(bool_field_type))
assertFieldType('null_bool_field', 'models.{}(blank=True, null=True)'.format(bool_field_type))
if connection.features.can_introspect_decimal_field:
if connection.vendor != 'sqlite':
assertFieldType('decimal_field', "models.DecimalField(max_digits=6, decimal_places=1)")
else: # Guessed arguments on SQLite, see #5014
assertFieldType('decimal_field', "models.DecimalField(max_digits=10, decimal_places=5) "
@ -118,37 +116,11 @@ class InspectDBTestCase(TestCase):
"as this database handles decimal fields as float")
assertFieldType('float_field', "models.FloatField()")
assertFieldType('int_field', "models.IntegerField()")
if connection.features.can_introspect_positive_integer_field:
assertFieldType('pos_int_field', "models.PositiveIntegerField()")
else:
assertFieldType('pos_int_field', "models.IntegerField()")
if connection.features.can_introspect_positive_integer_field:
if connection.features.can_introspect_big_integer_field:
assertFieldType('pos_big_int_field', 'models.PositiveBigIntegerField()')
else:
assertFieldType('pos_big_int_field', 'models.PositiveIntegerField()')
if connection.features.can_introspect_small_integer_field:
assertFieldType('pos_small_int_field', "models.PositiveSmallIntegerField()")
else:
assertFieldType('pos_small_int_field', "models.PositiveIntegerField()")
else:
if connection.features.can_introspect_big_integer_field:
assertFieldType('pos_big_int_field', 'models.BigIntegerField()')
else:
assertFieldType('pos_big_int_field', 'models.IntegerField()')
if connection.features.can_introspect_small_integer_field:
assertFieldType('pos_small_int_field', "models.SmallIntegerField()")
else:
assertFieldType('pos_small_int_field', "models.IntegerField()")
if connection.features.can_introspect_small_integer_field:
assertFieldType('small_int_field', "models.SmallIntegerField()")
else:
assertFieldType('small_int_field', "models.IntegerField()")
assertFieldType('pos_int_field', 'models.%s()' % introspected_field_types['PositiveIntegerField'])
assertFieldType('pos_big_int_field', 'models.%s()' % introspected_field_types['PositiveBigIntegerField'])
assertFieldType('pos_small_int_field', 'models.%s()' % introspected_field_types['PositiveSmallIntegerField'])
assertFieldType('small_int_field', 'models.%s()' % introspected_field_types['SmallIntegerField'])
@skipUnlessDBFeature('can_introspect_foreign_keys')
def test_attribute_name_not_python_keyword(self):

View File

@ -84,10 +84,10 @@ class IntrospectionTests(TransactionTestCase):
'CharField',
'CharField',
'CharField',
'BigIntegerField' if connection.features.can_introspect_big_integer_field else 'IntegerField',
'BinaryField' if connection.features.can_introspect_binary_field else 'TextField',
'SmallIntegerField' if connection.features.can_introspect_small_integer_field else 'IntegerField',
'DurationField' if connection.features.can_introspect_duration_field else 'BigIntegerField',
connection.features.introspected_field_types['BigIntegerField'],
connection.features.introspected_field_types['BinaryField'],
connection.features.introspected_field_types['SmallIntegerField'],
connection.features.introspected_field_types['DurationField'],
]
)
@ -113,7 +113,7 @@ class IntrospectionTests(TransactionTestCase):
with connection.cursor() as cursor:
desc = connection.introspection.get_table_description(cursor, City._meta.db_table)
self.assertIn(
connection.features.introspected_big_auto_field_type,
connection.features.introspected_field_types['BigAutoField'],
[connection.introspection.get_field_type(r[1], r) for r in desc],
)
@ -122,7 +122,7 @@ class IntrospectionTests(TransactionTestCase):
with connection.cursor() as cursor:
desc = connection.introspection.get_table_description(cursor, Country._meta.db_table)
self.assertIn(
connection.features.introspected_small_auto_field_type,
connection.features.introspected_field_types['SmallAutoField'],
[connection.introspection.get_field_type(r[1], r) for r in desc],
)

View File

@ -559,7 +559,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_field_types['BooleanField'])
def test_add_field_default_transform(self):
"""