Opened 3 years ago
Last modified 3 years ago
#33673 closed Bug
Django silently removes constraints when removing columns, then arbitrarily choses to include them in migrations — at Version 1
Reported by: | George | Owned by: | nobody |
---|---|---|---|
Component: | Migrations | Version: | 3.2 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
I’ve encountered an odd bug(?) in Django in our production code. Dropping a column manually in migrations *without* removing its constraints leads to Django being unaware those constraints have been removed, and auto-generating incorrect migrations.
A short example:
Creating the migrations for this schema:
class M(): a = models.IntegerField(default=1) b = models.IntegerField(default=2) class Meta: constraints = [ UniqueConstraint( name="uniq_ab_1”, fields=[“a”, “b”] ) ]
Create the constraint uniq_ab_1
is Postgres as expected (when using the \d+
command on the table to visualize)
However, this manual migration will remove the constraint, due to one of its member columns being deleted, this is just standard Postgres behavior:
migrations.RemoveField( model_name=“m”, name=“a”, ), migrations.AddField( model_name=“m”, name=“a”, field=models.IntegerField(default=6), ),
This migration runs just fine. I can even modify the field of M
again and run a further migration. However, using \d+
reveals that the uniq_ab_1
constraint` is gone from the database.
The only way I found out about this behavior was by renaming the constraint to uniq_ab_2
, then auto-generating migrations and getting the error:
django.db.utils.ProgrammingError: constraint "uniq_ab_1" of relation … does not exist
. In other words, on a rename, Django become aware a rename was happening and tried to remove the constraint from the database, in spite of itt being gone for a few migrations.
This behavior is rather unexpected. I’d assume Django would either:
- Notice the code model differs from the database schema in the original migration (when the constraint gets inadvertently removed) and fail
- Notice that the constraint is missing in the next migration (e.g. when adding an arbitrary field to
M
) and try to add it back again.
As it stands it seems like this ghost constraint is observed by some migration operations and not others.
Is this a known bug in Django?
Is there a way to guard against this behavior?
Is this perfectly normal and I am doing something wrong?