Skip to content

Commit

Permalink
Add DropIndex operation
Browse files Browse the repository at this point in the history
Also tests added, changes in states.py
  • Loading branch information
akki committed Jun 5, 2016
1 parent 6ace47c commit a929506
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 25 deletions.
3 changes: 3 additions & 0 deletions django/db/backends/base/schema.py
Expand Up @@ -355,6 +355,9 @@ def create_index(self, model, index):
self.execute(index.as_sql(self, suffix='_idx'))

def drop_index(self, model, index):
"""
Delete an index from model.
"""
self.execute(index.remove_sql(self))

def _delete_composed_index(self, model, fields, constraint_kwargs, sql):
Expand Down
8 changes: 4 additions & 4 deletions django/db/migrations/operations/__init__.py
@@ -1,15 +1,15 @@
from .fields import AddField, AlterField, RemoveField, RenameField
from .models import (
CreateIndex, AlterIndexTogether, AlterModelManagers, AlterModelOptions,
AlterModelTable, AlterOrderWithRespectTo, AlterUniqueTogether, CreateModel,
DeleteModel, RenameModel,
AlterIndexTogether, AlterModelManagers, AlterModelOptions,
AlterModelTable, AlterOrderWithRespectTo, AlterUniqueTogether, CreateIndex,
CreateModel, DeleteModel, DropIndex, RenameModel,
)
from .special import RunPython, RunSQL, SeparateDatabaseAndState

__all__ = [
'CreateModel', 'DeleteModel', 'AlterModelTable', 'AlterUniqueTogether',
'RenameModel', 'AlterIndexTogether', 'AlterModelOptions', 'CreateIndex',
'AddField', 'RemoveField', 'AlterField', 'RenameField',
'DropIndex', 'AddField', 'RemoveField', 'AlterField', 'RenameField',
'SeparateDatabaseAndState', 'RunSQL', 'RunPython',
'AlterOrderWithRespectTo', 'AlterModelManagers',
]
47 changes: 28 additions & 19 deletions django/db/migrations/operations/models.py
Expand Up @@ -742,8 +742,11 @@ def deconstruct(self):

def state_forwards(self, app_label, state):
model_state = state.models[app_label, self.model_name.lower()]
model_state.options['indexes'] = getattr(model_state.options, 'indexes', []).append(self.index)
# model_state.options['indexes'] = getattr(model_state.options, 'indexes', set()).add(self.index)
if not hasattr(self.index, 'model'):
self.index.model = state.apps.get_model(app_label, self.model_name)
if not 'indexes' in model_state.options:
model_state.options['indexes'] = []
model_state.options['indexes'].append(self.index)

def database_forwards(self, app_label, schema_editor, from_state, to_state):
to_model = to_state.apps.get_model(app_label, self.model_name)
Expand All @@ -768,14 +771,16 @@ def describe(self):

class DropIndex(Operation):
"""
Removes an index.
Deletes an index of a model.
"""

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

def deconstruct(self):
kwargs = {
'model_name': self.model_name,
'name': self.name
}
return (
Expand All @@ -786,28 +791,32 @@ def deconstruct(self):

def state_forwards(self, app_label, state):
model_state = state.models[app_label, self.model_name.lower()]
# model_state.options['indexes'] = getattr(model_state.options, 'indexes', [])

if not hasattr(model_state.options, 'indexes'):
model_state.options['indexes'] = set()
model_state.options['indexes'].add(self.index)
i, idx = model_state.get_index_by_name(self.name)
idx_list = model_state.options['indexes']
model_state.options['indexes'] = idx_list[:i] + idx_list[(i+1):]
# del model_state.options['indexes'][i]
# model_state.options['indexes'].remove(idx)

def database_forwards(self, app_label, schema_editor, from_state, to_state):
model = to_state.apps.get_model(app_label, self.model_name)
schema_editor.create_index(
model,
self.index,
to_model = to_state.apps.get_model(app_label, self.model_name)
from_model_state = from_state.models[app_label, self.model_name.lower()]
_, index = from_model_state.get_index_by_name(self.name)
schema_editor.drop_index(
to_model,
index,
)

def database_backwards(self, app_label, schema_editor, from_state, to_state):
model = to_state.apps.get_model(app_label, self.model_name)
schema_editor.drop_index(
model,
self.index,
from_model = from_state.apps.get_model(app_label, self.model_name)
to_model_state = to_state.models[app_label, self.model_name.lower()]
_, index = to_model_state.get_index_by_name(self.name)
schema_editor.create_index(
from_model,
index,
)

def describe(self):
return "Create index on field(s) %s of model %s" % (
', '.join(self.index.fields),
return "Remove index %s from %s" % (
self.name,
self.model_name,
)
7 changes: 7 additions & 0 deletions django/db/migrations/state.py
Expand Up @@ -554,6 +554,13 @@ def get_field_by_name(self, name):
return field
raise ValueError("No field called %s on model %s" % (name, self.name))

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

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

Expand Down
2 changes: 1 addition & 1 deletion django/db/models/__init__.py
Expand Up @@ -11,13 +11,13 @@
)
from django.db.models.fields import * # NOQA
from django.db.models.fields.files import FileField, ImageField # NOQA
from django.db.models.indexes import *
from django.db.models.fields.proxy import OrderWrt # 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
Prefetch, Q, QuerySet, prefetch_related_objects,
)
from django.db.models.indexes import *

# Imports that would create circular imports if sorted
from django.db.models.base import DEFERRED, Model # NOQA isort:skip
Expand Down
1 change: 1 addition & 0 deletions django/db/models/indexes.py
@@ -1,3 +1,4 @@

__all__ = ['Index']

class Index(object):
Expand Down
35 changes: 34 additions & 1 deletion tests/migrations/test_operations.py
Expand Up @@ -51,7 +51,7 @@ def make_test_state(self, app_label, operation, **kwargs):
return project_state, new_state

def set_up_test_model(
self, app_label, second_model=False, third_model=False,
self, app_label, second_model=False, third_model=False, index=False,
related_model=False, mti_model=False, proxy_model=False, manager_model=False,
unique_together=False, options=False, db_table=None, index_together=False):
"""
Expand Down Expand Up @@ -96,6 +96,11 @@ def set_up_test_model(
],
options=model_options,
)]
if index:
operations.append(migrations.CreateIndex(
"Pony",
models.Index("pink", name="Pony_test_idx")
))
if second_model:
operations.append(migrations.CreateModel(
"Stable",
Expand Down Expand Up @@ -1360,6 +1365,34 @@ def test_create_index(self):
self.assertEqual(definition[1], [])
self.assertEqual(definition[2], {'model_name': 'Pony', 'index': index})

def test_drop_index(self):
"""
Tests the DropIndex operation.
"""
project_state = self.set_up_test_model("test_rmin", index=True)
self.assertTableExists("test_rmin_pony")
self.assertIndexExists("test_rmin_pony", ["pink"])
# Test the state alteration
operation = migrations.DropIndex("Pony", "Pony_test_idx")
self.assertEqual(operation.describe(), "Remove index Pony_test_idx from Pony")
new_state = project_state.clone()
operation.state_forwards("test_rmin", new_state)
self.assertEqual(len(new_state.models["test_rmin", "pony"].options['indexes']), 0)
self.assertIndexExists("test_rmin_pony", ["pink"])
# Test the database alteration
with connection.schema_editor() as editor:
operation.database_forwards("test_rmin", editor, project_state, new_state)
self.assertIndexNotExists("test_rmin_pony", ["pink"])
# And test reversal
with connection.schema_editor() as editor:
operation.database_backwards("test_rmin", editor, new_state, project_state)
self.assertIndexExists("test_rmin_pony", ["pink"])
# And deconstruction
definition = operation.deconstruct()
self.assertEqual(definition[0], "DropIndex")
self.assertEqual(definition[1], [])
self.assertEqual(definition[2], {'model_name': "Pony", 'name': 'Pony_test_idx'})

def test_alter_index_together(self):
"""
Tests the AlterIndexTogether operation.
Expand Down

0 comments on commit a929506

Please sign in to comment.