| DEP: | xxxx |
|---|---|
| Author: | Marc Tamlyn, Akshesh Doshi |
| Status: | Draft |
| Type: | Feature |
| Created: | 2016-03-03 |
Table of Contents
This proposal aims to bring flexibility in Django on what it can do with db
indexes and allow Django to support more complex indexes on relevant backends
(most notably PostgreSQL), such as functional indexes and indexes with different
algorithms (e.g. GiST).
There are new fields being introduced in contrib.postgres which have
similar issues to the rationale for spatial_index, a BTree index is
either insufficent, inefficient or does not work and an alternative index type
is needed for that field. A field should be able to declare the type of index which
is appropriate to it.
Also indexes need to be made more customisable, like to be able to extend support for DEFERRABLE INITIALLY DEFERRED for UNIQUE constraints.
In addition, both postgres and oracle support powerful functional indexes
(and mysql supports indexing on the first few characters of a string), which
allow significant performance benefits for certain queries. Of course, it is
possible to create these indexes by hand if you know what you are doing using
RunSQL based migrations but this is a place for improvement.
PostgreSQL also supports partial indexes.
With the recent advances in expression syntax, they can be extended to indexes with modifications in the way indexes work.
Introduce django.db.models.indexes with deconstructable Index class.
This will be inherited by different index algorithms such as BTree and Hash,
and also FunctionalIndex(expression).
Index classes will take 3 optional arguments fields, model and name.
Both name of the field "slug" or the field object itself (self.slug) will be
allowed to be passed as arguments to fields.
Index() classes will be able to define their own SQL using the as_sql
pattern similar to that used by Lookup(), but will be passed to the
SchemaEditor class instead of a compiler. By default, this will defer to
the SchemaEditor class, but it allows non-standard index creation such as
that for spatialite (using a SELECT statement and a function) without
having to subclass SchemaEditor.
Introduce Meta.indexes for other indexes. This will contain a list of
Index() instances. index_together will continue to be supported but
will be translated into Meta.indexes internally and handled only there by
SchemaEditor.
The general workflow for index addition/deletion would be - migrations framework detects that Meta.indexes has changed => models layer tells migrations that due to the change a new index needs to be added/deleted => migrations framework implements this change.
In order to have a robust way to detect name changes in indexes, the entire index name will be explicitly passed from the operation to migrations, whether they are auto-generated or not.
A new method alter_indexes will get added to BaseDatabaseSchemaEditor
to detect any changes in indexes (including those created by index_together).
Therefore, alter_index_together will be deprecated.
db_index of the Field class will support index classes
(e.g. IntegerField(db_index=indexes.Hash)) other than the values
True and False. Passing an index not supported by the user's
backend to this argument would raise a NotImplementedError.
Field will gain a method get_default_index which will be called when
db_index=True is passed to the __init__. The default value would be
the SpatialIndex in gis, which will mark the start of deprecation of
spatial_index which would be supported with deprecation warnings.
db_index, index_together would actually become shortcuts for
editing Meta.indexes. Also a new attribute index_type can be added
to Field which can be used to use an index other than the default index
for that field and would be ignored if db_index is False, if that level
of customisability is required.
Index() subclasses will also have a supports(backend) method which
returns True or False depending on whether the backend supports that
index type.
Example:
class Town(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
region = gis.PolygonField(db_index=True)
area = models.FloatField()
population_density = models.FloatField()
class Meta:
indexes = [
models.BTree(fields=['slug', self.name]),
models.FunctionalIndex(expression=(F('area') * F('population_density')),
type=models.BTree),
]
spatial_index will be deprecated as setting db_index on a spatial field
will be sufficient.
alter_index_together would get deprecated in favour of the more generic method
alter_indexes.
This document has been placed in the public domain per the Creative Commons CC0 1.0 Universal license (http://creativecommons.org/publicdomain/zero/1.0/deed).
(All DEPs must include this exact copyright statement.)