#33673: Django silently removes constraints when removing columns, then 
arbitrarily
choses to include them in migrations
----------------------------+--------------------------------------
     Reporter:  George3d6   |                    Owner:  nobody
         Type:  Bug         |                   Status:  new
    Component:  Migrations  |                  Version:  3.2
     Severity:  Normal      |               Resolution:
     Keywords:              |             Triage Stage:  Unreviewed
    Has patch:  0           |      Needs documentation:  0
  Needs tests:  0           |  Patch needs improvement:  0
Easy pickings:  0           |                    UI/UX:  0
----------------------------+--------------------------------------
Description changed by George3d6:

Old description:

> 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:
> 1. Notice the code model differs from the database schema in the original
> migration (when the constraint gets inadvertently removed) and fail
> 2. 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?

New description:

 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:
 1. Notice the code model differs from the database schema in the original
 migration (when the constraint gets inadvertently removed) and fail
 2. 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?

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/33673#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/010701807bcfad55-7f78e44b-9de5-4fc9-9014-5ffca24add3d-000000%40eu-central-1.amazonses.com.

Reply via email to