#35991: Migrations crash on SQLite when renaming part of CompositePrimaryKey.
-------------------------------------+-------------------------------------
Reporter: Mariusz Felisiak | Owner: Mariusz
| Felisiak
Type: Bug | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Release blocker | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Mariusz Felisiak):
Replying to [comment:2 Simon Charette]:
> Thanks for the report!
>
> The issue likely lies in `ProjectState.rename_field` and in
`MigrationAutoDetector.create_renamed_fields` which shouldn't generate the
`AlterField` against the composite primary key in the first place.
In my case the second migration has three operations:
{{{
migrations.RenameField(
model_name='release',
old_name='name',
new_name='name2',
),
migrations.AddField(
model_name='release',
name='rename',
field=models.CharField(default='x', max_length=20),
preserve_default=False,
),
migrations.AlterField(
model_name='release',
name='pk',
field=models.CompositePrimaryKey('version', 'rename',
blank=True, editable=False, primary_key=True, serialize=False),
),
}}}
`AddField` crashes like in the ticket description, not `AlterField`, at
least on SQLite where it tries to remake a table. I think we should update
field references for `CompositePrimaryKey` before remaking a table. The
following draft patch fixes this crash for me:
{{{#!diff
diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py
index 6cf675d0e4..9a4549f1f1 100644
--- a/django/db/migrations/state.py
+++ b/django/db/migrations/state.py
@@ -11,6 +11,7 @@ from django.core.exceptions import FieldDoesNotExist
from django.db import models
from django.db.migrations.utils import field_is_referenced,
get_references
from django.db.models import NOT_PROVIDED
+from django.db.models.fields.composite import CompositePrimaryKey
from django.db.models.fields.related import
RECURSIVE_RELATIONSHIP_CONSTANT
from django.db.models.options import DEFAULT_NAMES, normalize_together
from django.db.models.utils import make_model_tuple
@@ -355,6 +356,15 @@ class ProjectState:
field = to_model[model_key].pop(old_name_lower)
field.name = new_name_lower
to_model[model_key][new_name_lower] = field
+ # Fix field references in a composite primary key.
+ for field_name, field in model_state.fields.items():
+ if isinstance(field, CompositePrimaryKey):
+ if old_name in field.field_names:
+ field.field_names = tuple(
+ new_name if sub_field_name == old_name else
sub_field_name
+ for sub_field_name in field.field_names
+ )
+
self.reload_model(*model_key, delay=delay)
def _find_reload_model(self, app_label, model_name, delay=False):
}}}
What do you think?
--
Ticket URL: <
https://code.djangoproject.com/ticket/35991#comment:5>