summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshesh <aksheshdoshi@gmail.com>2016-07-22 18:22:44 +0530
committerTim Graham <timograham@gmail.com>2016-08-12 15:52:16 -0400
commit311a8e8d505049ff5644a94e16c00246c8a43a18 (patch)
treeb1414effbd64ec472f64e3484b63d20d4b554526
parent6b842c599865217529d835c0e20e881b98104295 (diff)
Fixed #20888 -- Added support for column order in class-based indexes.
-rw-r--r--django/db/models/indexes.py23
-rw-r--r--docs/ref/models/indexes.txt14
-rw-r--r--tests/model_indexes/tests.py5
-rw-r--r--tests/schema/tests.py24
4 files changed, 60 insertions, 6 deletions
diff --git a/django/db/models/indexes.py b/django/db/models/indexes.py
index e3f94f0bba..bb33f8a758 100644
--- a/django/db/models/indexes.py
+++ b/django/db/models/indexes.py
@@ -17,6 +17,11 @@ class Index(object):
if not fields:
raise ValueError('At least one field is required to define an index.')
self.fields = fields
+ # A list of 2-tuple with the field name and ordering ('' or 'DESC').
+ self.fields_orders = [
+ (field_name[1:], 'DESC') if field_name.startswith('-') else (field_name, '')
+ for field_name in self.fields
+ ]
self.name = name or ''
if self.name:
errors = self.check_name()
@@ -38,15 +43,17 @@ class Index(object):
return errors
def create_sql(self, model, schema_editor):
- fields = [model._meta.get_field(field) for field in self.fields]
+ fields = [model._meta.get_field(field_name) for field_name, order in self.fields_orders]
tablespace_sql = schema_editor._get_index_tablespace_sql(model, fields)
- columns = [field.column for field in fields]
-
quote_name = schema_editor.quote_name
+ columns = [
+ ('%s %s' % (quote_name(field.column), order)).strip()
+ for field, (field_name, order) in zip(fields, self.fields_orders)
+ ]
return schema_editor.sql_create_index % {
'table': quote_name(model._meta.db_table),
'name': quote_name(self.name),
- 'columns': ', '.join(quote_name(column) for column in columns),
+ 'columns': ', '.join(columns),
'extra': tablespace_sql,
}
@@ -82,8 +89,12 @@ class Index(object):
fit its size by truncating the excess length.
"""
table_name = model._meta.db_table
- column_names = [model._meta.get_field(field).column for field in self.fields]
- hash_data = [table_name] + column_names + [self.suffix]
+ column_names = [model._meta.get_field(field_name).column for field_name, order in self.fields_orders]
+ column_names_with_order = [
+ (('-%s' if order else '%s') % column_name)
+ for column_name, (field_name, order) in zip(column_names, self.fields_orders)
+ ]
+ hash_data = [table_name] + column_names_with_order + [self.suffix]
self.name = '%s_%s_%s' % (
table_name[:11],
column_names[0][:7],
diff --git a/docs/ref/models/indexes.txt b/docs/ref/models/indexes.txt
index b63e86517c..ac2839bbd4 100644
--- a/docs/ref/models/indexes.txt
+++ b/docs/ref/models/indexes.txt
@@ -34,6 +34,20 @@ options`_.
A list of the name of the fields on which the index is desired.
+By default, indexes are created with an ascending order for each column. To
+define an index with a descending order for a column, add a hyphen before the
+field's name.
+
+For example ``Index(fields=['headline', '-pub_date'])`` would create SQL with
+``(headline, pub_date DESC)``. Index ordering isn't supported on MySQL. In that
+case, a descending index is created as a normal index.
+
+.. admonition:: Support for column ordering on SQLite
+
+ Column ordering is supported on SQLite 3.3.0+ and only for some database
+ file formats. Refer to the `SQLite docs
+ <https://www.sqlite.org/lang_createindex.html>`_ for specifics.
+
``name``
--------
diff --git a/tests/model_indexes/tests.py b/tests/model_indexes/tests.py
index b3f5d04dc1..52db494905 100644
--- a/tests/model_indexes/tests.py
+++ b/tests/model_indexes/tests.py
@@ -46,6 +46,11 @@ class IndexesTests(TestCase):
index.set_name_with_model(Book)
self.assertEqual(index.name, 'model_index_author_0f5565_idx')
+ # '-' for DESC columns should be accounted for in the index name.
+ index = models.Index(fields=['-author'])
+ index.set_name_with_model(Book)
+ self.assertEqual(index.name, 'model_index_author_708765_idx')
+
# fields may be truncated in the name. db_column is used for naming.
long_field_index = models.Index(fields=['pages'])
long_field_index.set_name_with_model(Book)
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index 6bb4aa6f8f..9622a443a9 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -156,6 +156,12 @@ class SchemaTests(TransactionTestCase):
counts['indexes'] += 1
return counts
+ def assertIndexOrder(self, table, index, order):
+ constraints = self.get_constraints(table)
+ self.assertIn(index, constraints)
+ index_orders = constraints[index]['orders']
+ self.assertTrue(all([(val == expected) for val, expected in zip(index_orders, order)]))
+
# Tests
def test_creation_deletion(self):
"""
@@ -1597,6 +1603,24 @@ class SchemaTests(TransactionTestCase):
editor.remove_index(Author, index)
self.assertNotIn('name', self.get_indexes(Author._meta.db_table))
+ def test_order_index(self):
+ """
+ Indexes defined with ordering (ASC/DESC) defined on column
+ """
+ with connection.schema_editor() as editor:
+ editor.create_model(Author)
+ # The table doesn't have an index
+ self.assertNotIn('title', self.get_indexes(Author._meta.db_table))
+ index_name = 'author_name_idx'
+ # Add the index
+ index = Index(fields=['name', '-weight'], name=index_name)
+ with connection.schema_editor() as editor:
+ editor.add_index(Author, index)
+ if connection.features.supports_index_column_ordering:
+ if connection.features.uppercases_column_names:
+ index_name = index_name.upper()
+ self.assertIndexOrder(Author._meta.db_table, index_name, ['ASC', 'DESC'])
+
def test_indexes(self):
"""
Tests creation/altering of indexes