mirror of
https://github.com/django/django.git
synced 2025-06-05 11:39:13 +00:00
Fixed #31630 -- Replaced introspection features with DatabaseFeatures.introspected_field_types.
This commit is contained in:
parent
55556e51fb
commit
e198beadad
@ -128,38 +128,22 @@ class BaseDatabaseFeatures:
|
|||||||
# Can the backend introspect an AutoField, instead of an IntegerField?
|
# Can the backend introspect an AutoField, instead of an IntegerField?
|
||||||
can_introspect_autofield = False
|
can_introspect_autofield = False
|
||||||
|
|
||||||
# Can the backend introspect a BigIntegerField, instead of an IntegerField?
|
# Map fields which some backends may not be able to differentiate to the
|
||||||
can_introspect_big_integer_field = True
|
# field it's introspected as.
|
||||||
|
introspected_field_types = {
|
||||||
# Can the backend introspect an BinaryField, instead of an TextField?
|
'BigAutoField': 'BigAutoField',
|
||||||
can_introspect_binary_field = True
|
'BigIntegerField': 'BigIntegerField',
|
||||||
|
'BinaryField': 'BinaryField',
|
||||||
# Can the backend introspect an DecimalField, instead of an FloatField?
|
'BooleanField': 'BooleanField',
|
||||||
can_introspect_decimal_field = True
|
'DurationField': 'DurationField',
|
||||||
|
'GenericIPAddressField': 'GenericIPAddressField',
|
||||||
# Can the backend introspect a DurationField, instead of a BigIntegerField?
|
'PositiveBigIntegerField': 'PositiveBigIntegerField',
|
||||||
can_introspect_duration_field = True
|
'PositiveIntegerField': 'PositiveIntegerField',
|
||||||
|
'PositiveSmallIntegerField': 'PositiveSmallIntegerField',
|
||||||
# Can the backend introspect an IPAddressField, instead of an CharField?
|
'SmallAutoField': 'SmallAutoField',
|
||||||
can_introspect_ip_address_field = False
|
'SmallIntegerField': 'SmallIntegerField',
|
||||||
|
'TimeField': 'TimeField',
|
||||||
# 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'
|
|
||||||
|
|
||||||
# Can the backend introspect the column order (ASC/DESC) for indexes?
|
# Can the backend introspect the column order (ASC/DESC) for indexes?
|
||||||
supports_index_column_ordering = True
|
supports_index_column_ordering = True
|
||||||
|
@ -15,11 +15,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
supports_regex_backreferencing = False
|
supports_regex_backreferencing = False
|
||||||
supports_date_lookup_using_string = False
|
supports_date_lookup_using_string = False
|
||||||
can_introspect_autofield = True
|
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_index_column_ordering = False
|
||||||
supports_timezones = False
|
supports_timezones = False
|
||||||
requires_explicit_null_ordering_when_grouping = True
|
requires_explicit_null_ordering_when_grouping = True
|
||||||
@ -70,6 +65,16 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
"Confirm support for introspected foreign keys"
|
"Confirm support for introspected foreign keys"
|
||||||
return self._mysql_storage_engine != 'MyISAM'
|
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
|
@cached_property
|
||||||
def can_return_columns_from_insert(self):
|
def can_return_columns_from_insert(self):
|
||||||
return self.connection.mysql_is_mariadb and self.connection.mysql_version >= (10, 5, 0)
|
return self.connection.mysql_is_mariadb and self.connection.mysql_version >= (10, 5, 0)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.db import InterfaceError
|
from django.db import InterfaceError
|
||||||
from django.db.backends.base.features import BaseDatabaseFeatures
|
from django.db.backends.base.features import BaseDatabaseFeatures
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
|
||||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
@ -22,7 +23,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
supports_tablespaces = True
|
supports_tablespaces = True
|
||||||
supports_sequence_reset = False
|
supports_sequence_reset = False
|
||||||
can_introspect_materialized_views = True
|
can_introspect_materialized_views = True
|
||||||
can_introspect_time_field = False
|
|
||||||
atomic_transactions = False
|
atomic_transactions = False
|
||||||
supports_combined_alters = False
|
supports_combined_alters = False
|
||||||
nulls_order_largest = True
|
nulls_order_largest = True
|
||||||
@ -61,3 +61,15 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
allows_multiple_constraints_on_same_fields = False
|
allows_multiple_constraints_on_same_fields = False
|
||||||
supports_boolean_expr_in_select_clause = False
|
supports_boolean_expr_in_select_clause = False
|
||||||
supports_primitives_in_json_field = 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',
|
||||||
|
}
|
||||||
|
@ -22,10 +22,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
can_release_savepoints = True
|
can_release_savepoints = True
|
||||||
supports_tablespaces = True
|
supports_tablespaces = True
|
||||||
supports_transactions = True
|
supports_transactions = True
|
||||||
can_introspect_autofield = True
|
|
||||||
can_introspect_ip_address_field = True
|
|
||||||
can_introspect_materialized_views = True
|
can_introspect_materialized_views = True
|
||||||
can_introspect_small_integer_field = True
|
|
||||||
can_distinct_on_fields = True
|
can_distinct_on_fields = True
|
||||||
can_rollback_ddl = True
|
can_rollback_ddl = True
|
||||||
supports_combined_alters = True
|
supports_combined_alters = True
|
||||||
@ -61,6 +58,15 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
supports_deferrable_unique_constraints = True
|
supports_deferrable_unique_constraints = True
|
||||||
has_json_operators = 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
|
@cached_property
|
||||||
def is_postgresql_10(self):
|
def is_postgresql_10(self):
|
||||||
return self.connection.pg_version >= 100000
|
return self.connection.pg_version >= 100000
|
||||||
|
@ -19,12 +19,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
max_query_params = 999
|
max_query_params = 999
|
||||||
supports_mixed_date_datetime_comparisons = False
|
supports_mixed_date_datetime_comparisons = False
|
||||||
can_introspect_autofield = True
|
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
|
supports_transactions = True
|
||||||
atomic_transactions = False
|
atomic_transactions = False
|
||||||
can_rollback_ddl = True
|
can_rollback_ddl = True
|
||||||
@ -51,6 +45,16 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0)
|
supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0)
|
||||||
order_by_nulls_first = True
|
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
|
@cached_property
|
||||||
def supports_json_field(self):
|
def supports_json_field(self):
|
||||||
try:
|
try:
|
||||||
|
@ -247,7 +247,20 @@ Database backend API
|
|||||||
This section describes changes that may be needed in third-party database
|
This section describes changes that may be needed in third-party database
|
||||||
backends.
|
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`
|
:mod:`django.contrib.gis`
|
||||||
-------------------------
|
-------------------------
|
||||||
|
@ -60,6 +60,7 @@ class InspectDBTestCase(TestCase):
|
|||||||
def test_field_types(self):
|
def test_field_types(self):
|
||||||
"""Test introspection of various Django field types"""
|
"""Test introspection of various Django field types"""
|
||||||
assertFieldType = self.make_field_type_asserter()
|
assertFieldType = self.make_field_type_asserter()
|
||||||
|
introspected_field_types = connection.features.introspected_field_types
|
||||||
|
|
||||||
# Inspecting Oracle DB doesn't produce correct results (#19884):
|
# Inspecting Oracle DB doesn't produce correct results (#19884):
|
||||||
# - it reports fields as blank=True when they aren't.
|
# - 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('url_field', "models.CharField(max_length=200)")
|
||||||
assertFieldType('date_field', "models.DateField()")
|
assertFieldType('date_field', "models.DateField()")
|
||||||
assertFieldType('date_time_field', "models.DateTimeField()")
|
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()")
|
assertFieldType('gen_ip_address_field', "models.GenericIPAddressField()")
|
||||||
elif not connection.features.interprets_empty_strings_as_nulls:
|
elif not connection.features.interprets_empty_strings_as_nulls:
|
||||||
assertFieldType('gen_ip_address_field', "models.CharField(max_length=39)")
|
assertFieldType('gen_ip_address_field', "models.CharField(max_length=39)")
|
||||||
if connection.features.can_introspect_time_field:
|
assertFieldType('time_field', 'models.%s()' % introspected_field_types['TimeField'])
|
||||||
assertFieldType('time_field', "models.TimeField()")
|
|
||||||
if connection.features.has_native_uuid_field:
|
if connection.features.has_native_uuid_field:
|
||||||
assertFieldType('uuid_field', "models.UUIDField()")
|
assertFieldType('uuid_field', "models.UUIDField()")
|
||||||
elif not connection.features.interprets_empty_strings_as_nulls:
|
elif not connection.features.interprets_empty_strings_as_nulls:
|
||||||
@ -97,20 +97,18 @@ class InspectDBTestCase(TestCase):
|
|||||||
def test_number_field_types(self):
|
def test_number_field_types(self):
|
||||||
"""Test introspection of various Django field types"""
|
"""Test introspection of various Django field types"""
|
||||||
assertFieldType = self.make_field_type_asserter()
|
assertFieldType = self.make_field_type_asserter()
|
||||||
|
introspected_field_types = connection.features.introspected_field_types
|
||||||
|
|
||||||
if not connection.features.can_introspect_autofield:
|
if not connection.features.can_introspect_autofield:
|
||||||
assertFieldType('id', "models.IntegerField(primary_key=True) # AutoField?")
|
assertFieldType('id', "models.IntegerField(primary_key=True) # AutoField?")
|
||||||
|
|
||||||
if connection.features.can_introspect_big_integer_field:
|
assertFieldType('big_int_field', 'models.%s()' % introspected_field_types['BigIntegerField'])
|
||||||
assertFieldType('big_int_field', "models.BigIntegerField()")
|
|
||||||
else:
|
|
||||||
assertFieldType('big_int_field', "models.IntegerField()")
|
|
||||||
|
|
||||||
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('bool_field', "models.{}()".format(bool_field_type))
|
||||||
assertFieldType('null_bool_field', 'models.{}(blank=True, null=True)'.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)")
|
assertFieldType('decimal_field', "models.DecimalField(max_digits=6, decimal_places=1)")
|
||||||
else: # Guessed arguments on SQLite, see #5014
|
else: # Guessed arguments on SQLite, see #5014
|
||||||
assertFieldType('decimal_field', "models.DecimalField(max_digits=10, decimal_places=5) "
|
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")
|
"as this database handles decimal fields as float")
|
||||||
|
|
||||||
assertFieldType('float_field', "models.FloatField()")
|
assertFieldType('float_field', "models.FloatField()")
|
||||||
|
|
||||||
assertFieldType('int_field', "models.IntegerField()")
|
assertFieldType('int_field', "models.IntegerField()")
|
||||||
|
assertFieldType('pos_int_field', 'models.%s()' % introspected_field_types['PositiveIntegerField'])
|
||||||
if connection.features.can_introspect_positive_integer_field:
|
assertFieldType('pos_big_int_field', 'models.%s()' % introspected_field_types['PositiveBigIntegerField'])
|
||||||
assertFieldType('pos_int_field', "models.PositiveIntegerField()")
|
assertFieldType('pos_small_int_field', 'models.%s()' % introspected_field_types['PositiveSmallIntegerField'])
|
||||||
else:
|
assertFieldType('small_int_field', 'models.%s()' % introspected_field_types['SmallIntegerField'])
|
||||||
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()")
|
|
||||||
|
|
||||||
@skipUnlessDBFeature('can_introspect_foreign_keys')
|
@skipUnlessDBFeature('can_introspect_foreign_keys')
|
||||||
def test_attribute_name_not_python_keyword(self):
|
def test_attribute_name_not_python_keyword(self):
|
||||||
|
@ -84,10 +84,10 @@ class IntrospectionTests(TransactionTestCase):
|
|||||||
'CharField',
|
'CharField',
|
||||||
'CharField',
|
'CharField',
|
||||||
'CharField',
|
'CharField',
|
||||||
'BigIntegerField' if connection.features.can_introspect_big_integer_field else 'IntegerField',
|
connection.features.introspected_field_types['BigIntegerField'],
|
||||||
'BinaryField' if connection.features.can_introspect_binary_field else 'TextField',
|
connection.features.introspected_field_types['BinaryField'],
|
||||||
'SmallIntegerField' if connection.features.can_introspect_small_integer_field else 'IntegerField',
|
connection.features.introspected_field_types['SmallIntegerField'],
|
||||||
'DurationField' if connection.features.can_introspect_duration_field else 'BigIntegerField',
|
connection.features.introspected_field_types['DurationField'],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ class IntrospectionTests(TransactionTestCase):
|
|||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
desc = connection.introspection.get_table_description(cursor, City._meta.db_table)
|
desc = connection.introspection.get_table_description(cursor, City._meta.db_table)
|
||||||
self.assertIn(
|
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],
|
[connection.introspection.get_field_type(r[1], r) for r in desc],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ class IntrospectionTests(TransactionTestCase):
|
|||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
desc = connection.introspection.get_table_description(cursor, Country._meta.db_table)
|
desc = connection.introspection.get_table_description(cursor, Country._meta.db_table)
|
||||||
self.assertIn(
|
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],
|
[connection.introspection.get_field_type(r[1], r) for r in desc],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -559,7 +559,7 @@ class SchemaTests(TransactionTestCase):
|
|||||||
columns = self.column_classes(Author)
|
columns = self.column_classes(Author)
|
||||||
# BooleanField are stored as TINYINT(1) on MySQL.
|
# BooleanField are stored as TINYINT(1) on MySQL.
|
||||||
field_type = columns['awesome'][0]
|
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):
|
def test_add_field_default_transform(self):
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user