diff options
| author | Akshesh <aksheshdoshi@gmail.com> | 2016-06-20 21:20:05 +0530 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2016-08-05 18:12:51 -0400 |
| commit | 6a8372e6ec42eb50e05f9dbcb26b783237bdc233 (patch) | |
| tree | b0ea6c93f189009bf54bead1a7d665c27f3c6744 /django | |
| parent | d117567c7d65c3c28858c4dfc771483b182075e4 (diff) | |
Fixed #26808 -- Added Meta.indexes for class-based indexes.
* Added the index name to its deconstruction.
* Added indexes to sqlite3.schema._remake_table() so that indexes
aren't dropped when _remake_table() is called.
Thanks timgraham & MarkusH for review and advice.
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/backends/base/schema.py | 7 | ||||
| -rw-r--r-- | django/db/backends/sqlite3/schema.py | 8 | ||||
| -rw-r--r-- | django/db/migrations/autodetector.py | 60 | ||||
| -rw-r--r-- | django/db/migrations/state.py | 7 | ||||
| -rw-r--r-- | django/db/models/base.py | 7 | ||||
| -rw-r--r-- | django/db/models/indexes.py | 2 | ||||
| -rw-r--r-- | django/db/models/options.py | 1 |
7 files changed, 89 insertions, 3 deletions
diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index b936b61fb8..39a7e5e623 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -889,8 +889,8 @@ class BaseDatabaseSchemaEditor(object): def _model_indexes_sql(self, model): """ - Return all index SQL statements (field indexes, index_together) for the - specified model, as a list. + Return all index SQL statements (field indexes, index_together, + Meta.indexes) for the specified model, as a list. """ if not model._meta.managed or model._meta.proxy or model._meta.swapped: return [] @@ -901,6 +901,9 @@ class BaseDatabaseSchemaEditor(object): for field_names in model._meta.index_together: fields = [model._meta.get_field(field) for field in field_names] output.append(self._create_index_sql(model, fields, suffix="_idx")) + + for index in model._meta.indexes: + output.append(index.create_sql(model, self)) return output def _field_indexes_sql(self, model, field): diff --git a/django/db/backends/sqlite3/schema.py b/django/db/backends/sqlite3/schema.py index 1a9fb1523a..36adb22584 100644 --- a/django/db/backends/sqlite3/schema.py +++ b/django/db/backends/sqlite3/schema.py @@ -156,12 +156,20 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): for index in model._meta.index_together ] + indexes = model._meta.indexes + if delete_field: + indexes = [ + index for index in indexes + if delete_field.name not in index.fields + ] + # Construct a new model for the new state meta_contents = { 'app_label': model._meta.app_label, 'db_table': model._meta.db_table, 'unique_together': unique_together, 'index_together': index_together, + 'indexes': indexes, 'apps': apps, } meta = type("Meta", tuple(), meta_contents) diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 3c82473fe1..a0146710e6 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -126,6 +126,7 @@ class MigrationAutodetector(object): # We'll then go through that list later and order it and split # into migrations to resolve dependencies caused by M2Ms and FKs. self.generated_operations = {} + self.altered_indexes = {} # Prepare some old/new state and model lists, separating # proxy models and ignoring unmigrated apps. @@ -175,6 +176,12 @@ class MigrationAutodetector(object): self.generate_altered_options() self.generate_altered_managers() + # Create the altered indexes and store them in self.altered_indexes. + # This avoids the same computation in generate_removed_indexes() + # and generate_added_indexes(). + self.create_altered_indexes() + # Generate index removal operations before field is removed + self.generate_removed_indexes() # Generate field operations self.generate_renamed_fields() self.generate_removed_fields() @@ -182,6 +189,7 @@ class MigrationAutodetector(object): self.generate_altered_fields() self.generate_altered_unique_together() self.generate_altered_index_together() + self.generate_added_indexes() self.generate_altered_db_table() self.generate_altered_order_with_respect_to() @@ -521,6 +529,9 @@ class MigrationAutodetector(object): related_fields[field.name] = field if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created: related_fields[field.name] = field + # Are there any indexes to defer? + indexes = model_state.options['indexes'] + model_state.options['indexes'] = [] # Are there unique/index_together to defer? unique_together = model_state.options.pop('unique_together', None) index_together = model_state.options.pop('index_together', None) @@ -581,6 +592,15 @@ class MigrationAutodetector(object): for name, field in sorted(related_fields.items()) ] related_dependencies.append((app_label, model_name, None, True)) + for index in indexes: + self.add_operation( + app_label, + operations.AddIndex( + model_name=model_name, + index=index, + ), + dependencies=related_dependencies, + ) if unique_together: self.add_operation( app_label, @@ -919,6 +939,46 @@ class MigrationAutodetector(object): self._generate_removed_field(app_label, model_name, field_name) self._generate_added_field(app_label, model_name, field_name) + def create_altered_indexes(self): + option_name = operations.AddIndex.option_name + for app_label, model_name in sorted(self.kept_model_keys): + old_model_name = self.renamed_models.get((app_label, model_name), model_name) + old_model_state = self.from_state.models[app_label, old_model_name] + new_model_state = self.to_state.models[app_label, model_name] + + old_indexes = old_model_state.options[option_name] + new_indexes = new_model_state.options[option_name] + add_idx = [idx for idx in new_indexes if idx not in old_indexes] + rem_idx = [idx for idx in old_indexes if idx not in new_indexes] + + self.altered_indexes.update({ + (app_label, model_name): { + 'added_indexes': add_idx, 'removed_indexes': rem_idx, + } + }) + + def generate_added_indexes(self): + for (app_label, model_name), alt_indexes in self.altered_indexes.items(): + for index in alt_indexes['added_indexes']: + self.add_operation( + app_label, + operations.AddIndex( + model_name=model_name, + index=index, + ) + ) + + def generate_removed_indexes(self): + for (app_label, model_name), alt_indexes in self.altered_indexes.items(): + for index in alt_indexes['removed_indexes']: + self.add_operation( + app_label, + operations.RemoveIndex( + model_name=model_name, + name=index.name, + ) + ) + def _get_dependencies_for_foreign_key(self, field): # Account for FKs to swappable models swappable_setting = getattr(field, 'swappable_setting', None) diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py index 12ec9ffdb8..32cfc96afb 100644 --- a/django/db/migrations/state.py +++ b/django/db/migrations/state.py @@ -353,6 +353,13 @@ class ModelState(object): 'ModelState.fields cannot refer to a model class - "%s.through" does. ' 'Use a string reference instead.' % name ) + # Sanity-check that indexes have their name set. + for index in self.options['indexes']: + if not index.name: + raise ValueError( + "Indexes passed to ModelState require a name attribute. " + "%r doesn't have one." % index + ) @cached_property def name_lower(self): diff --git a/django/db/models/base.py b/django/db/models/base.py index 80d98b1399..e15ca00609 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -298,6 +298,13 @@ class ModelBase(type): else: new_class.add_to_class(field.name, copy.deepcopy(field)) + # Set the name of _meta.indexes. This can't be done in + # Options.contribute_to_class() because fields haven't been added to + # the model at that point. + for index in new_class._meta.indexes: + if not index.name: + index.set_name_with_model(new_class) + if abstract: # Abstract base models can't be instantiated and don't appear in # the list of models for an app. We do the final setup for them a diff --git a/django/db/models/indexes.py b/django/db/models/indexes.py index 694d904a87..e3f94f0bba 100644 --- a/django/db/models/indexes.py +++ b/django/db/models/indexes.py @@ -60,7 +60,7 @@ class Index(object): def deconstruct(self): path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__) path = path.replace('django.db.models.indexes', 'django.db.models') - return (path, (), {'fields': self.fields}) + return (path, (), {'fields': self.fields, 'name': self.name}) @staticmethod def _hash_generator(*args): diff --git a/django/db/models/options.py b/django/db/models/options.py index 19dce58663..670e2a5a2f 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -99,6 +99,7 @@ class Options(object): self.db_table = '' self.ordering = [] self._ordering_clash = False + self.indexes = [] self.unique_together = [] self.index_together = [] self.select_on_save = False |
