mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	[1.11.x] Fixed #28043 -- Prevented AddIndex and RemoveIndex from mutating model state.
Backport of 63afe3a2bf from master
			
			
This commit is contained in:
		| @@ -777,7 +777,10 @@ class AddIndex(IndexOperation): | |||||||
|  |  | ||||||
|     def state_forwards(self, app_label, state): |     def state_forwards(self, app_label, state): | ||||||
|         model_state = state.models[app_label, self.model_name_lower] |         model_state = state.models[app_label, self.model_name_lower] | ||||||
|         model_state.options[self.option_name].append(self.index) |         indexes = list(model_state.options[self.option_name]) | ||||||
|  |         indexes.append(self.index.clone()) | ||||||
|  |         model_state.options[self.option_name] = indexes | ||||||
|  |         state.reload_model(app_label, self.model_name_lower, delay=True) | ||||||
|  |  | ||||||
|     def database_forwards(self, app_label, schema_editor, from_state, to_state): |     def database_forwards(self, app_label, schema_editor, from_state, to_state): | ||||||
|         model = to_state.apps.get_model(app_label, self.model_name) |         model = to_state.apps.get_model(app_label, self.model_name) | ||||||
| @@ -821,6 +824,7 @@ class RemoveIndex(IndexOperation): | |||||||
|         model_state = state.models[app_label, self.model_name_lower] |         model_state = state.models[app_label, self.model_name_lower] | ||||||
|         indexes = model_state.options[self.option_name] |         indexes = model_state.options[self.option_name] | ||||||
|         model_state.options[self.option_name] = [idx for idx in indexes if idx.name != self.name] |         model_state.options[self.option_name] = [idx for idx in indexes if idx.name != self.name] | ||||||
|  |         state.reload_model(app_label, self.model_name_lower, delay=True) | ||||||
|  |  | ||||||
|     def database_forwards(self, app_label, schema_editor, from_state, to_state): |     def database_forwards(self, app_label, schema_editor, from_state, to_state): | ||||||
|         model = from_state.apps.get_model(app_label, self.model_name) |         model = from_state.apps.get_model(app_label, self.model_name) | ||||||
|   | |||||||
| @@ -584,6 +584,9 @@ class ModelState(object): | |||||||
|             app_label=self.app_label, |             app_label=self.app_label, | ||||||
|             name=self.name, |             name=self.name, | ||||||
|             fields=list(self.fields), |             fields=list(self.fields), | ||||||
|  |             # Since options are shallow-copied here, operations such as | ||||||
|  |             # AddIndex must replace their option (e.g 'indexes') rather | ||||||
|  |             # than mutating it. | ||||||
|             options=dict(self.options), |             options=dict(self.options), | ||||||
|             bases=self.bases, |             bases=self.bases, | ||||||
|             managers=list(self.managers), |             managers=list(self.managers), | ||||||
|   | |||||||
| @@ -69,3 +69,6 @@ Bugfixes | |||||||
|  |  | ||||||
| * Fixed crash in ``BaseGeometryWidget.get_context()`` when overriding existing | * Fixed crash in ``BaseGeometryWidget.get_context()`` when overriding existing | ||||||
|   ``attrs`` (:ticket:`28105`). |   ``attrs`` (:ticket:`28105`). | ||||||
|  |  | ||||||
|  | * Prevented ``AddIndex`` and ``RemoveIndex`` from mutating model state | ||||||
|  |   (:ticket:`28043`). | ||||||
|   | |||||||
| @@ -1491,6 +1491,29 @@ class OperationTests(OperationTestBase): | |||||||
|         self.unapply_operations("test_rmin", project_state, operations=operations) |         self.unapply_operations("test_rmin", project_state, operations=operations) | ||||||
|         self.assertIndexExists("test_rmin_pony", ["pink", "weight"]) |         self.assertIndexExists("test_rmin_pony", ["pink", "weight"]) | ||||||
|  |  | ||||||
|  |     def test_add_index_state_forwards(self): | ||||||
|  |         project_state = self.set_up_test_model('test_adinsf') | ||||||
|  |         index = models.Index(fields=['pink'], name='test_adinsf_pony_pink_idx') | ||||||
|  |         old_model = project_state.apps.get_model('test_adinsf', 'Pony') | ||||||
|  |         new_state = project_state.clone() | ||||||
|  |  | ||||||
|  |         operation = migrations.AddIndex('Pony', index) | ||||||
|  |         operation.state_forwards('test_adinsf', new_state) | ||||||
|  |         new_model = new_state.apps.get_model('test_adinsf', 'Pony') | ||||||
|  |         self.assertIsNot(old_model, new_model) | ||||||
|  |  | ||||||
|  |     def test_remove_index_state_forwards(self): | ||||||
|  |         project_state = self.set_up_test_model('test_rminsf') | ||||||
|  |         index = models.Index(fields=['pink'], name='test_rminsf_pony_pink_idx') | ||||||
|  |         migrations.AddIndex('Pony', index).state_forwards('test_rminsf', project_state) | ||||||
|  |         old_model = project_state.apps.get_model('test_rminsf', 'Pony') | ||||||
|  |         new_state = project_state.clone() | ||||||
|  |  | ||||||
|  |         operation = migrations.RemoveIndex('Pony', 'test_rminsf_pony_pink_idx') | ||||||
|  |         operation.state_forwards('test_rminsf', new_state) | ||||||
|  |         new_model = new_state.apps.get_model('test_rminsf', 'Pony') | ||||||
|  |         self.assertIsNot(old_model, new_model) | ||||||
|  |  | ||||||
|     def test_alter_field_with_index(self): |     def test_alter_field_with_index(self): | ||||||
|         """ |         """ | ||||||
|         Test AlterField operation with an index to ensure indexes created via |         Test AlterField operation with an index to ensure indexes created via | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user