#29000 closed Bug (fixed)
RenameModel does not rename M2M column when run after RenameField
Reported by: | David Nelson Adamec | Owned by: | Jeff |
---|---|---|---|
Component: | Migrations | Version: | 2.0 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Encountered this on a project at work. I have some models like so:
class ModelA(models.Model): name_renamed = models.CharField(max_length=10) class ModelBRenamed(models.Model): a = models.ForeignKey(ModelA, null=True) class ModelC(models.Model): b_set = models.ManyToManyField(ModelBRenamed)
I have a migration 0002_renamefield to rename a field on ModelA:
class Migration(migrations.Migration): dependencies = [ ('myapp', '0001_initial'), ] operations = [ migrations.RenameField( model_name='modela', old_name='name', new_name='name_renamed', ), ]
Then a migration 0003_renamemodel to rename ModelB:
class Migration(migrations.Migration): dependencies = [ ('myapp', '0002_renamefield'), ] operations = [ migrations.RenameModel( old_name='ModelB', new_name='ModelBRenamed', ), ]
If the two migrations are run together, then the modelb_id
column in myapp_modelc_b_set
will not be renamed to modelbrenamed_id
. However, if I run the migrations one at a time, the column will be renamed as expected.
I think this is related to #27737. When ModelA is reloaded in the RenameField migration, ModelB is reloaded due to its foreign key but is missing its relationship to ModelC. When the RenameModel migration is run, ModelC is not included in ModelB's related_objects, so the through table's column is not renamed.
I've reproduced this using Django 1.11 on either MySQL or SQLite.
Change History (13)
follow-up: 2 comment:1 by , 7 years ago
Component: | Uncategorized → Migrations |
---|---|
Type: | Uncategorized → Bug |
comment:2 by , 7 years ago
Sorry, should have just done that from the start. Tested in Django 2.0.1, and the issue still occurs.
comment:3 by , 7 years ago
Description: | modified (diff) |
---|---|
Version: | 1.11 → 2.0 |
comment:4 by , 7 years ago
Triage Stage: | Unreviewed → Accepted |
---|
Thanks, good bug report. I can also reproduce on PostgreSQL with Django master (e2908ecb3e20a629be801ddb826c474f7e7023ea).
comment:5 by , 7 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:6 by , 7 years ago
After doing a little more digging, it appears that the reload_model
with delay=True
in the RenameField migration is the culprit. Because of the delay
flag, only the original model and its directly related models (ModelA and ModelB in the example) are reloaded, so ModelC is untouched and its b_set
field continues to point at the old version of ModelB that's no longer registered. Since ModelC isn't pointing at the new ModelB, it's not included in ModelB's related_objects
.
I'm not sure what the performance implication would be if delayed loading were removed from RenameField/AlterField, but I'm sure it's not good. Perhaps there's another clever solution out there, but I feel a bit out of my depth. Here's the workaround I'm using for the time being.
class RenameModelWorkaround(migrations.RenameModel): def database_forwards(self, app_label, schema_editor, from_state, to_state): from_state.reload_model(app_label, self.old_name_lower) super(RenameModelWorkaround, self).database_forwards( app_label, schema_editor, from_state, to_state)
comment:7 by , 7 years ago
Owner: | removed |
---|---|
Status: | assigned → new |
comment:8 by , 7 years ago
Owner: | set to |
---|---|
Status: | new → assigned |
Unsurprisingly, this bug also breaks backwards migrations, as the names in the bridge table don't match expected values.
For the models/migrations in the tickets description, backwards migrations produces the expected result:
django.db.utils.OperationalError: (1054, "Unknown column 'modelbrenamed_id' in 'temp_modelc_b_set'")
David Nelson Adamec was also correct, removing 'delay=delay' from the state.reload_model()
call in state_forwards()
did fix this particular issue, but I am unsure as to what downsides are associated with removing the delay. I am looking into other solutions now.
comment:11 by , 7 years ago
Summary: | RenameModel does not rename M2M column when run after AlterField/RenameField → RenameModel does not rename M2M column when run after RenameField |
---|---|
Triage Stage: | Accepted → Ready for checkin |
Please test with Django 2.0 as there are changes there which may resolve this.