Commit 156e2d59 authored by Akshesh's avatar Akshesh Committed by Tim Graham
Browse files

Fixed #26709 -- Added class-based indexes.

Added the AddIndex and RemoveIndex operations to use them in migrations.

Thanks markush, mjtamlyn, timgraham, and charettes for review and advice.
parent c962b910
Loading
Loading
Loading
Loading
+21 −6
Original line number Diff line number Diff line
@@ -316,6 +316,18 @@ class BaseDatabaseSchemaEditor(object):
            "table": self.quote_name(model._meta.db_table),
        })

    def add_index(self, index):
        """
        Add an index on a model.
        """
        self.execute(index.create_sql(self))

    def remove_index(self, index):
        """
        Remove an index from a model.
        """
        self.execute(index.remove_sql(self))

    def alter_unique_together(self, model, old_unique_together, new_unique_together):
        """
        Deals with a model changing its unique_together.
@@ -836,12 +848,7 @@ class BaseDatabaseSchemaEditor(object):
            index_name = "D%s" % index_name[:-1]
        return index_name

    def _create_index_sql(self, model, fields, suffix="", sql=None):
        """
        Return the SQL statement to create the index for one or several fields.
        `sql` can be specified if the syntax differs from the standard (GIS
        indexes, ...).
        """
    def _get_index_tablespace_sql(self, model, fields):
        if len(fields) == 1 and fields[0].db_tablespace:
            tablespace_sql = self.connection.ops.tablespace_sql(fields[0].db_tablespace)
        elif model._meta.db_tablespace:
@@ -850,7 +857,15 @@ class BaseDatabaseSchemaEditor(object):
            tablespace_sql = ""
        if tablespace_sql:
            tablespace_sql = " " + tablespace_sql
        return tablespace_sql

    def _create_index_sql(self, model, fields, suffix="", sql=None):
        """
        Return the SQL statement to create the index for one or several fields.
        `sql` can be specified if the syntax differs from the standard (GIS
        indexes, ...).
        """
        tablespace_sql = self._get_index_tablespace_sql(model, fields)
        columns = [field.column for field in fields]
        sql_create_index = sql or self.sql_create_index
        return sql_create_index % {
+5 −5
Original line number Diff line number Diff line
from .fields import AddField, AlterField, RemoveField, RenameField
from .models import (
    AlterIndexTogether, AlterModelManagers, AlterModelOptions, AlterModelTable,
    AlterOrderWithRespectTo, AlterUniqueTogether, CreateModel, DeleteModel,
    RenameModel,
    AddIndex, AlterIndexTogether, AlterModelManagers, AlterModelOptions,
    AlterModelTable, AlterOrderWithRespectTo, AlterUniqueTogether, CreateModel,
    DeleteModel, RemoveIndex, RenameModel,
)
from .special import RunPython, RunSQL, SeparateDatabaseAndState

__all__ = [
    'CreateModel', 'DeleteModel', 'AlterModelTable', 'AlterUniqueTogether',
    'RenameModel', 'AlterIndexTogether', 'AlterModelOptions',
    'AddField', 'RemoveField', 'AlterField', 'RenameField',
    'RenameModel', 'AlterIndexTogether', 'AlterModelOptions', 'AddIndex',
    'RemoveIndex', 'AddField', 'RemoveField', 'AlterField', 'RenameField',
    'SeparateDatabaseAndState', 'RunSQL', 'RunPython',
    'AlterOrderWithRespectTo', 'AlterModelManagers',
]
+77 −0
Original line number Diff line number Diff line
@@ -742,3 +742,80 @@ class AlterModelManagers(ModelOptionOperation):

    def describe(self):
        return "Change managers on %s" % (self.name, )


class AddIndex(Operation):
    """
    Add an index on a model.
    """

    def __init__(self, model_name, index):
        self.model_name = model_name
        self.index = index

    def state_forwards(self, app_label, state):
        model_state = state.models[app_label, self.model_name.lower()]
        self.index.model = state.apps.get_model(app_label, self.model_name)
        model_state.options['indexes'].append(self.index)

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        schema_editor.add_index(self.index)

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        schema_editor.remove_index(self.index)

    def deconstruct(self):
        kwargs = {
            'model_name': self.model_name,
            'index': self.index,
        }
        return (
            self.__class__.__name__,
            [],
            kwargs,
        )

    def describe(self):
        return 'Create index on field(s) %s of model %s' % (
            ', '.join(self.index.fields),
            self.model_name,
        )


class RemoveIndex(Operation):
    """
    Remove an index from a model.
    """

    def __init__(self, model_name, name):
        self.model_name = model_name
        self.name = name

    def state_forwards(self, app_label, state):
        model_state = state.models[app_label, self.model_name.lower()]
        indexes = model_state.options['indexes']
        model_state.options['indexes'] = [idx for idx in indexes if idx.name != self.name]

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        from_model_state = from_state.models[app_label, self.model_name.lower()]
        index = from_model_state.get_index_by_name(self.name)
        schema_editor.remove_index(index)

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        to_model_state = to_state.models[app_label, self.model_name.lower()]
        index = to_model_state.get_index_by_name(self.name)
        schema_editor.add_index(index)

    def deconstruct(self):
        kwargs = {
            'model_name': self.model_name,
            'name': self.name,
        }
        return (
            self.__class__.__name__,
            [],
            kwargs,
        )

    def describe(self):
        return 'Remove index %s from %s' % (self.name, self.model_name)
+7 −0
Original line number Diff line number Diff line
@@ -330,6 +330,7 @@ class ModelState(object):
        self.name = force_text(name)
        self.fields = fields
        self.options = options or {}
        self.options.setdefault('indexes', [])
        self.bases = bases or (models.Model, )
        self.managers = managers or []
        # Sanity-check that fields is NOT a dict. It must be ordered.
@@ -557,6 +558,12 @@ class ModelState(object):
                return field
        raise ValueError("No field called %s on model %s" % (name, self.name))

    def get_index_by_name(self, name):
        for index in self.options['indexes']:
            if index.name == name:
                return index
        raise ValueError("No index named %s on model %s" % (name, self.name))

    def __repr__(self):
        return "<ModelState: '%s.%s'>" % (self.app_label, self.name)

+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ from django.db.models.expressions import ( # NOQA
from django.db.models.fields import *  # NOQA
from django.db.models.fields.files import FileField, ImageField  # NOQA
from django.db.models.fields.proxy import OrderWrt  # NOQA
from django.db.models.indexes import *  # NOQA
from django.db.models.lookups import Lookup, Transform  # NOQA
from django.db.models.manager import Manager  # NOQA
from django.db.models.query import (  # NOQA
Loading