Kaydet (Commit) d3891256 authored tarafından Claude Paroz's avatar Claude Paroz

Fixed #27098 -- Deprecated DatabaseIntrospection.get_indexes

Thanks Akshesh <aksheshdoshi@gmail.com> for help with the PostgreSQL query.
Thanks Tim Graham for the review.
üst 1ec1633c
...@@ -61,11 +61,18 @@ class SpatiaLiteIntrospection(DatabaseIntrospection): ...@@ -61,11 +61,18 @@ class SpatiaLiteIntrospection(DatabaseIntrospection):
return field_type, field_params return field_type, field_params
def get_indexes(self, cursor, table_name): def get_constraints(self, cursor, table_name):
indexes = super(SpatiaLiteIntrospection, self).get_indexes(cursor, table_name) constraints = super(SpatiaLiteIntrospection, self).get_constraints(cursor, table_name)
cursor.execute('SELECT f_geometry_column ' cursor.execute('SELECT f_geometry_column '
'FROM geometry_columns ' 'FROM geometry_columns '
'WHERE f_table_name=%s AND spatial_index_enabled=1', (table_name,)) 'WHERE f_table_name=%s AND spatial_index_enabled=1', (table_name,))
for row in cursor.fetchall(): for row in cursor.fetchall():
indexes[row[0]] = {'primary_key': False, 'unique': False} constraints['%s__spatial__index' % row[0]] = {
return indexes "columns": [row[0]],
"primary_key": False,
"unique": False,
"foreign_key": None,
"check": False,
"index": True,
}
return constraints
...@@ -70,14 +70,15 @@ class Command(BaseCommand): ...@@ -70,14 +70,15 @@ class Command(BaseCommand):
relations = connection.introspection.get_relations(cursor, table_name) relations = connection.introspection.get_relations(cursor, table_name)
except NotImplementedError: except NotImplementedError:
relations = {} relations = {}
try:
indexes = connection.introspection.get_indexes(cursor, table_name)
except NotImplementedError:
indexes = {}
try: try:
constraints = connection.introspection.get_constraints(cursor, table_name) constraints = connection.introspection.get_constraints(cursor, table_name)
except NotImplementedError: except NotImplementedError:
constraints = {} constraints = {}
primary_key_column = connection.introspection.get_primary_key_column(cursor, table_name)
unique_columns = [
c['columns'][0] for c in constraints.values()
if c['unique'] and len(c['columns']) == 1
]
table_description = connection.introspection.get_table_description(cursor, table_name) table_description = connection.introspection.get_table_description(cursor, table_name)
except Exception as e: except Exception as e:
yield "# Unable to inspect table '%s'" % table_name yield "# Unable to inspect table '%s'" % table_name
...@@ -105,11 +106,10 @@ class Command(BaseCommand): ...@@ -105,11 +106,10 @@ class Command(BaseCommand):
column_to_field_name[column_name] = att_name column_to_field_name[column_name] = att_name
# Add primary_key and unique, if necessary. # Add primary_key and unique, if necessary.
if column_name in indexes: if column_name == primary_key_column:
if indexes[column_name]['primary_key']: extra_params['primary_key'] = True
extra_params['primary_key'] = True elif column_name in unique_columns:
elif indexes[column_name]['unique']: extra_params['unique'] = True
extra_params['unique'] = True
if is_relation: if is_relation:
rel_to = ( rel_to = (
......
from collections import namedtuple from collections import namedtuple
from django.utils import six
# Structure returned by DatabaseIntrospection.get_table_list() # Structure returned by DatabaseIntrospection.get_table_list()
TableInfo = namedtuple('TableInfo', ['name', 'type']) TableInfo = namedtuple('TableInfo', ['name', 'type'])
...@@ -143,13 +141,14 @@ class BaseDatabaseIntrospection(object): ...@@ -143,13 +141,14 @@ class BaseDatabaseIntrospection(object):
""" """
Returns the name of the primary key column for the given table. Returns the name of the primary key column for the given table.
""" """
for column in six.iteritems(self.get_indexes(cursor, table_name)): for constraint in self.get_constraints(cursor, table_name).values():
if column[1]['primary_key']: if constraint['primary_key']:
return column[0] return constraint['columns'][0]
return None return None
def get_indexes(self, cursor, table_name): def get_indexes(self, cursor, table_name):
""" """
Deprecated in Django 1.11, use get_constraints instead.
Returns a dictionary of indexed fieldname -> infodict for the given Returns a dictionary of indexed fieldname -> infodict for the given
table, where each infodict is in the format: table, where each infodict is in the format:
{'primary_key': boolean representing whether it's the primary key, {'primary_key': boolean representing whether it's the primary key,
......
import warnings
from collections import namedtuple from collections import namedtuple
from MySQLdb.constants import FIELD_TYPE from MySQLdb.constants import FIELD_TYPE
...@@ -6,6 +7,7 @@ from django.db.backends.base.introspection import ( ...@@ -6,6 +7,7 @@ from django.db.backends.base.introspection import (
BaseDatabaseIntrospection, FieldInfo, TableInfo, BaseDatabaseIntrospection, FieldInfo, TableInfo,
) )
from django.utils.datastructures import OrderedSet from django.utils.datastructures import OrderedSet
from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.encoding import force_text from django.utils.encoding import force_text
FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('extra', 'default')) FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('extra', 'default'))
...@@ -122,6 +124,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): ...@@ -122,6 +124,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
return key_columns return key_columns
def get_indexes(self, cursor, table_name): def get_indexes(self, cursor, table_name):
warnings.warn(
"get_indexes() is deprecated in favor of get_constraints().",
RemovedInDjango21Warning, stacklevel=2
)
cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name)) cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name))
# Do a two-pass search for indexes: on first pass check which indexes # Do a two-pass search for indexes: on first pass check which indexes
# are multicolumn, on second pass check which single-column indexes # are multicolumn, on second pass check which single-column indexes
......
import warnings
import cx_Oracle import cx_Oracle
from django.db.backends.base.introspection import ( from django.db.backends.base.introspection import (
BaseDatabaseIntrospection, FieldInfo, TableInfo, BaseDatabaseIntrospection, FieldInfo, TableInfo,
) )
from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.encoding import force_text from django.utils.encoding import force_text
...@@ -117,6 +120,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): ...@@ -117,6 +120,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
for row in cursor.fetchall()] for row in cursor.fetchall()]
def get_indexes(self, cursor, table_name): def get_indexes(self, cursor, table_name):
warnings.warn(
"get_indexes() is deprecated in favor of get_constraints().",
RemovedInDjango21Warning, stacklevel=2
)
sql = """ sql = """
SELECT LOWER(uic1.column_name) AS column_name, SELECT LOWER(uic1.column_name) AS column_name,
CASE user_constraints.constraint_type CASE user_constraints.constraint_type
......
from __future__ import unicode_literals from __future__ import unicode_literals
import warnings
from collections import namedtuple from collections import namedtuple
from django.db.backends.base.introspection import ( from django.db.backends.base.introspection import (
BaseDatabaseIntrospection, FieldInfo, TableInfo, BaseDatabaseIntrospection, FieldInfo, TableInfo,
) )
from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.encoding import force_text from django.utils.encoding import force_text
FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('default',)) FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('default',))
...@@ -124,6 +126,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): ...@@ -124,6 +126,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
return key_columns return key_columns
def get_indexes(self, cursor, table_name): def get_indexes(self, cursor, table_name):
warnings.warn(
"get_indexes() is deprecated in favor of get_constraints().",
RemovedInDjango21Warning, stacklevel=2
)
# This query retrieves each index on the given table, including the # This query retrieves each index on the given table, including the
# first associated field name # first associated field name
cursor.execute(self._get_indexes_query, [table_name]) cursor.execute(self._get_indexes_query, [table_name])
......
import re import re
import warnings
from collections import namedtuple from collections import namedtuple
from django.db.backends.base.introspection import ( from django.db.backends.base.introspection import (
BaseDatabaseIntrospection, FieldInfo, TableInfo, BaseDatabaseIntrospection, FieldInfo, TableInfo,
) )
from django.utils.deprecation import RemovedInDjango21Warning
field_size_re = re.compile(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$') field_size_re = re.compile(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$')
FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('default',)) FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('default',))
...@@ -183,6 +185,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): ...@@ -183,6 +185,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
return key_columns return key_columns
def get_indexes(self, cursor, table_name): def get_indexes(self, cursor, table_name):
warnings.warn(
"get_indexes() is deprecated in favor of get_constraints().",
RemovedInDjango21Warning, stacklevel=2
)
indexes = {} indexes = {}
for info in self._table_info(cursor, table_name): for info in self._table_info(cursor, table_name):
if info['pk'] != 0: if info['pk'] != 0:
......
...@@ -36,6 +36,8 @@ details on these changes. ...@@ -36,6 +36,8 @@ details on these changes.
* Silencing of exceptions raised while rendering the ``{% include %}`` template * Silencing of exceptions raised while rendering the ``{% include %}`` template
tag will be removed. tag will be removed.
* ``DatabaseIntrospection.get_indexes()`` will be removed.
.. _deprecation-removed-in-2.0: .. _deprecation-removed-in-2.0:
2.0 2.0
......
...@@ -399,6 +399,10 @@ Database backend API ...@@ -399,6 +399,10 @@ Database backend API
dictionaries with a list of ``'ASC'`` and/or ``'DESC'`` values corresponding dictionaries with a list of ``'ASC'`` and/or ``'DESC'`` values corresponding
to the the ordering of each column in the index. to the the ordering of each column in the index.
* :djadmin:`inspectdb` no longer calls ``DatabaseIntrospection.get_indexes()``
which is deprecated. Custom database backends should ensure all types of
indexes are returned by ``DatabaseIntrospection.get_constraints()``.
Dropped support for PostgreSQL 9.2 and PostGIS 2.0 Dropped support for PostgreSQL 9.2 and PostGIS 2.0
-------------------------------------------------- --------------------------------------------------
...@@ -549,3 +553,6 @@ Miscellaneous ...@@ -549,3 +553,6 @@ Miscellaneous
:ttag:`{% include %} <include>` template tag is deprecated as the behavior is :ttag:`{% include %} <include>` template tag is deprecated as the behavior is
often more confusing than helpful. In Django 2.1, the exception will be often more confusing than helpful. In Django 2.1, the exception will be
raised. raised.
* ``DatabaseIntrospection.get_indexes()`` is deprecated in favor of
``DatabaseIntrospection.get_constraints()``.
...@@ -69,8 +69,8 @@ class OperationTests(TransactionTestCase): ...@@ -69,8 +69,8 @@ class OperationTests(TransactionTestCase):
def assertSpatialIndexExists(self, table, column): def assertSpatialIndexExists(self, table, column):
with connection.cursor() as cursor: with connection.cursor() as cursor:
indexes = connection.introspection.get_indexes(cursor, table) constraints = connection.introspection.get_constraints(cursor, table)
self.assertIn(column, indexes) self.assertIn([column], [c['columns'] for c in constraints.values()])
def alter_gis_model(self, migration_class, model_name, field_name, def alter_gis_model(self, migration_class, model_name, field_name,
blank=False, field_class=None): blank=False, field_class=None):
......
...@@ -5,6 +5,8 @@ from unittest import skipUnless ...@@ -5,6 +5,8 @@ from unittest import skipUnless
from django.db import connection from django.db import connection
from django.db.utils import DatabaseError from django.db.utils import DatabaseError
from django.test import TransactionTestCase, mock, skipUnlessDBFeature from django.test import TransactionTestCase, mock, skipUnlessDBFeature
from django.test.utils import ignore_warnings
from django.utils.deprecation import RemovedInDjango21Warning
from .models import Article, ArticleReporter, City, District, Reporter from .models import Article, ArticleReporter, City, District, Reporter
...@@ -169,11 +171,13 @@ class IntrospectionTests(TransactionTestCase): ...@@ -169,11 +171,13 @@ class IntrospectionTests(TransactionTestCase):
self.assertEqual(primary_key_column, 'id') self.assertEqual(primary_key_column, 'id')
self.assertEqual(pk_fk_column, 'city_id') self.assertEqual(pk_fk_column, 'city_id')
@ignore_warnings(category=RemovedInDjango21Warning)
def test_get_indexes(self): def test_get_indexes(self):
with connection.cursor() as cursor: with connection.cursor() as cursor:
indexes = connection.introspection.get_indexes(cursor, Article._meta.db_table) indexes = connection.introspection.get_indexes(cursor, Article._meta.db_table)
self.assertEqual(indexes['reporter_id'], {'unique': False, 'primary_key': False}) self.assertEqual(indexes['reporter_id'], {'unique': False, 'primary_key': False})
@ignore_warnings(category=RemovedInDjango21Warning)
def test_get_indexes_multicol(self): def test_get_indexes_multicol(self):
""" """
Test that multicolumn indexes are not included in the introspection Test that multicolumn indexes are not included in the introspection
......
...@@ -483,9 +483,13 @@ class TestMigrations(TransactionTestCase): ...@@ -483,9 +483,13 @@ class TestMigrations(TransactionTestCase):
] ]
# Only the CharField should have a LIKE index. # Only the CharField should have a LIKE index.
self.assertEqual(like_constraint_columns_list, [['char2']]) self.assertEqual(like_constraint_columns_list, [['char2']])
with connection.cursor() as cursor:
indexes = connection.introspection.get_indexes(cursor, table_name)
# All fields should have regular indexes. # All fields should have regular indexes.
with connection.cursor() as cursor:
indexes = [
c['columns'][0]
for c in connection.introspection.get_constraints(cursor, table_name).values()
if c['index'] and len(c['columns']) == 1
]
self.assertIn('char', indexes) self.assertIn('char', indexes)
self.assertIn('char2', indexes) self.assertIn('char2', indexes)
self.assertIn('text', indexes) self.assertIn('text', indexes)
......
...@@ -105,12 +105,20 @@ class SchemaTests(TransactionTestCase): ...@@ -105,12 +105,20 @@ class SchemaTests(TransactionTestCase):
raise DatabaseError("Table does not exist (empty pragma)") raise DatabaseError("Table does not exist (empty pragma)")
return columns return columns
def get_primary_key(self, table):
with connection.cursor() as cursor:
return connection.introspection.get_primary_key_column(cursor, table)
def get_indexes(self, table): def get_indexes(self, table):
""" """
Get the indexes on the table using a new cursor. Get the indexes on the table using a new cursor.
""" """
with connection.cursor() as cursor: with connection.cursor() as cursor:
return connection.introspection.get_indexes(cursor, table) return [
c['columns'][0]
for c in connection.introspection.get_constraints(cursor, table).values()
if c['index'] and len(c['columns']) == 1
]
def get_constraints(self, table): def get_constraints(self, table):
""" """
...@@ -1685,9 +1693,7 @@ class SchemaTests(TransactionTestCase): ...@@ -1685,9 +1693,7 @@ class SchemaTests(TransactionTestCase):
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.create_model(Tag) editor.create_model(Tag)
# Ensure the table is there and has the right PK # Ensure the table is there and has the right PK
self.assertTrue( self.assertEqual(self.get_primary_key(Tag._meta.db_table), 'id')
self.get_indexes(Tag._meta.db_table)['id']['primary_key'],
)
# Alter to change the PK # Alter to change the PK
id_field = Tag._meta.get_field("id") id_field = Tag._meta.get_field("id")
old_field = Tag._meta.get_field("slug") old_field = Tag._meta.get_field("slug")
...@@ -1702,9 +1708,7 @@ class SchemaTests(TransactionTestCase): ...@@ -1702,9 +1708,7 @@ class SchemaTests(TransactionTestCase):
'id', 'id',
self.get_indexes(Tag._meta.db_table), self.get_indexes(Tag._meta.db_table),
) )
self.assertTrue( self.assertEqual(self.get_primary_key(Tag._meta.db_table), 'slug')
self.get_indexes(Tag._meta.db_table)['slug']['primary_key'],
)
def test_context_manager_exit(self): def test_context_manager_exit(self):
""" """
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment