Ticket #22432: 22432-prevent-m2m-repointing.patch
File 22432-prevent-m2m-repointing.patch, 7.2 KB (added by , 11 years ago) |
---|
-
django/db/backends/schema.py
diff --git a/django/db/backends/schema.py b/django/db/backends/schema.py index 2a62803..69bdc25 100644
a b class BaseDatabaseSchemaEditor(object): 481 481 old_type = old_db_params['type'] 482 482 new_db_params = new_field.db_parameters(connection=self.connection) 483 483 new_type = new_db_params['type'] 484 if old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and old_field.rel.through._meta.auto_created and new_field.rel.through._meta.auto_created): 484 if (old_type is None and new_type is None 485 and old_field.rel.through and new_field.rel.through 486 and old_field.rel.through._meta.auto_created 487 and new_field.rel.through._meta.auto_created 488 and old_field.rel.to._meta.db_table == new_field.rel.to._meta.db_table): 489 # ManyToMany with automatic through and no 'to=' change 485 490 return self._alter_many_to_many(model, old_field, new_field, strict) 486 491 elif old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and not old_field.rel.through._meta.auto_created and not new_field.rel.through._meta.auto_created): 487 492 # Both sides have through models; this is a no-op. … … class BaseDatabaseSchemaEditor(object): 491 496 old_field, 492 497 new_field, 493 498 )) 499 500 self._alter_field(model, old_field, new_field, old_type, new_type, old_db_params, new_db_params, strict) 501 502 def _alter_field(self, model, old_field, new_field, old_type, new_type, old_db_params, new_db_params, strict=False): 503 """Actually perform a "physical" (non-ManyToMany) field update.""" 504 494 505 # Has unique been removed? 495 506 if old_field.unique and (not new_field.unique or (not old_field.primary_key and new_field.primary_key)): 496 507 # Find the unique constraint for this field -
django/db/backends/sqlite3/schema.py
diff --git a/django/db/backends/sqlite3/schema.py b/django/db/backends/sqlite3/schema.py index 5b6155b..771fefb 100644
a b class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): 134 134 else: 135 135 self._remake_table(model, delete_fields=[field]) 136 136 137 def alter_field(self, model, old_field, new_field, strict=False): 138 """ 139 Allows a field's type, uniqueness, nullability, default, column, 140 constraints etc. to be modified. 141 Requires a copy of the old field as well so we can only perform 142 changes that are required. 143 If strict is true, raises errors if the old column does not match old_field precisely. 144 """ 145 old_db_params = old_field.db_parameters(connection=self.connection) 146 old_type = old_db_params['type'] 147 new_db_params = new_field.db_parameters(connection=self.connection) 148 new_type = new_db_params['type'] 149 if old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and old_field.rel.through._meta.auto_created and new_field.rel.through._meta.auto_created): 150 return self._alter_many_to_many(model, old_field, new_field, strict) 151 elif old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and not old_field.rel.through._meta.auto_created and not new_field.rel.through._meta.auto_created): 152 # Both sides have through models; this is a no-op. 153 return 154 elif old_type is None or new_type is None: 155 raise ValueError("Cannot alter field %s into %s - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)" % ( 156 old_field, 157 new_field, 158 )) 137 def _alter_field(self, model, old_field, new_field, old_type, new_type, old_db_params, new_db_params, strict=False): 159 138 # Alter by remaking table 160 139 self._remake_table(model, alter_fields=[(old_field, new_field)]) 161 140 -
tests/migrations/test_operations.py
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index 388544c..b5da089 100644
a b class OperationTests(MigrationTestBase): 34 34 with connection.schema_editor() as editor: 35 35 return migration.unapply(project_state, editor) 36 36 37 def set_up_test_model(self, app_label, second_model=False, related_model=False, mti_model=False):37 def set_up_test_model(self, app_label, second_model=False, third_model=False, related_model=False, mti_model=False): 38 38 """ 39 39 Creates a test model state and database table. 40 40 """ 41 41 # Delete the tables if they already exist 42 42 with connection.cursor() as cursor: 43 # Start with ManyToMany tables 44 try: 45 cursor.execute("DROP TABLE %s_pony_stables" % app_label) 46 except DatabaseError: 47 pass 48 try: 49 cursor.execute("DROP TABLE %s_pony_vans" % app_label) 50 except DatabaseError: 51 pass 52 53 # Then standard model tables 43 54 try: 44 55 cursor.execute("DROP TABLE %s_pony" % app_label) 45 56 except DatabaseError: … … class OperationTests(MigrationTestBase): 48 59 cursor.execute("DROP TABLE %s_stable" % app_label) 49 60 except DatabaseError: 50 61 pass 62 try: 63 cursor.execute("DROP TABLE %s_van" % app_label) 64 except DatabaseError: 65 pass 51 66 # Make the "current" state 52 67 operations = [migrations.CreateModel( 53 68 "Pony", … … class OperationTests(MigrationTestBase): 64 79 ("id", models.AutoField(primary_key=True)), 65 80 ] 66 81 )) 82 if third_model: 83 operations.append(migrations.CreateModel( 84 "Van", 85 [ 86 ("id", models.AutoField(primary_key=True)), 87 ] 88 )) 67 89 if related_model: 68 90 operations.append(migrations.CreateModel( 69 91 "Rider", … … class OperationTests(MigrationTestBase): 405 427 Pony = new_apps.get_model("test_alflmm", "Pony") 406 428 self.assertTrue(Pony._meta.get_field('stables').blank) 407 429 430 def test_repoint_field_m2m(self): 431 project_state = self.set_up_test_model("test_alflmm", second_model=True, third_model=True) 432 433 project_state = self.apply_operations("test_alflmm", project_state, operations=[ 434 migrations.AddField("Pony", "places", models.ManyToManyField("Stable", related_name="ponies")) 435 ]) 436 new_apps = project_state.render() 437 Pony = new_apps.get_model("test_alflmm", "Pony") 438 439 with self.assertRaises(ValueError): 440 # Changing the 'to=' of a ManyToManyField isn't allowed. 441 self.apply_operations("test_alflmm", project_state, operations=[ 442 migrations.AlterField("Pony", "places", models.ManyToManyField(to="Van", related_name="ponies")) 443 ]) 444 408 445 def test_remove_field_m2m(self): 409 446 project_state = self.set_up_test_model("test_rmflmm", second_model=True) 410 447