#36779: DeleteModel can lead to missing FK constraints
--------------------------------+--------------------------------------
     Reporter:  Jamie Cockburn  |                     Type:  Bug
       Status:  new             |                Component:  Migrations
      Version:  6.0             |                 Severity:  Normal
     Keywords:                  |             Triage Stage:  Unreviewed
    Has patch:  0               |      Needs documentation:  0
  Needs tests:  0               |  Patch needs improvement:  0
Easy pickings:  0               |                    UI/UX:  0
--------------------------------+--------------------------------------
 I have two models, `Jane` and `Bob`. `Jane` has ForeignKey relationship to
 `Bob`.

 I generate a migration 0001_initial:

 {{{#!python
 class Migration(migrations.Migration):
     operations = [
         migrations.CreateModel(
             name='Bob',
             fields=[
                 ('id', models.BigAutoField(auto_created=True,
 primary_key=True, serialize=False, verbose_name='ID')),
             ],
         ),
         migrations.CreateModel(
             name='Jane',
             fields=[
                 ('id', models.BigAutoField(auto_created=True,
 primary_key=True, serialize=False, verbose_name='ID')),
                 ('bob',
 models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
 to='constraintbug.bob')),
             ],
         ),
     ]
 }}}

 `Jane` is rendered in postgres as:

 {{{#!sql
 % docker compose exec db psql -U constraintbug
 psql (18.0 (Debian 18.0-1.pgdg13+3))
 Type "help" for help.

 constraintbug=# \d constraintbug_jane
                      Table "public.constraintbug_jane"
  Column |  Type  | Collation | Nullable |             Default
 --------+--------+-----------+----------+----------------------------------
  id     | bigint |           | not null | generated by default as identity
  bob_id | bigint |           | not null |
 Indexes:
     "constraintbug_jane_pkey" PRIMARY KEY, btree (id)
     "constraintbug_jane_bob_id_35b76f6b" btree (bob_id)
 Foreign-key constraints:
     "constraintbug_jane_bob_id_35b76f6b_fk_constraintbug_bob_id" FOREIGN
 KEY (bob_id) REFERENCES constraintbug_bob(id) DEFERRABLE INITIALLY
 DEFERRED
 }}}

 Note the FK constraint.

 Now, I decide to manually write a migration, because I want to remove
 `Bob` and replace it with a new thing. In my case, I was introducing model
 inheritance, and didn't want to revise my code to remove everything,
 `makemigrations`, add everything back,  `makemigrations` again, and I
 decided to just manually type `migrations.DeleteModel()`.

 {{{#!python
 class Migration(migrations.Migration):
     operations = [
         migrations.DeleteModel(  # this should fail?
             name='Bob',
         ),
     ]
 }}}

 Now things start to go wrong. I would expect at this point that the
 migration should fail, because I'm trying to delete a table that is
 referred to by the FK constraint. Instead, is just deletes the constraint:

 {{{#!sql
 constraintbug=# \d constraintbug_jane
                      Table "public.constraintbug_jane"
  Column |  Type  | Collation | Nullable |             Default
 --------+--------+-----------+----------+----------------------------------
  id     | bigint |           | not null | generated by default as identity
  bob_id | bigint |           | not null |
 Indexes:
     "constraintbug_jane_pkey" PRIMARY KEY, btree (id)
     "constraintbug_jane_bob_id_35b76f6b" btree (bob_id)
 }}}

 I suppose we could say that this is an "intermediate" state, but then, I
 go and add the model back:

 {{{#!python
 class Migration(migrations.Migration):
     operations = [
         migrations.CreateModel(  # maybe this should re-create the FK
 constraint on jane?
             name='Bob',
             fields=[
                 ('id', models.BigAutoField(auto_created=True,
 primary_key=True, serialize=False, verbose_name='ID')),
             ],
         ),
     ]
 }}}

 But `Jane` is still missing her constraint, and worse, I can do things
 like:
 {{{#!python
 Jane.objects.create(
     bob_id=1234,  # there is no such bob with pk 1234, this should fail!!!
 )
 }}}

 Here's a wee example repo showing the issue:
 https://github.com/daggaz/django-deletemodel/tree/master

 I think that either:
 * the `DeleteModel()` migration should fail to delete a model that is
 referenced
 * the 2nd `CreateModel()` migration should recreate the FK constraint on
 `Jane` that `DeleteModel()` deleted
 * at the very least the docs should have very big warning
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36779>
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 visit 
https://groups.google.com/d/msgid/django-updates/0107019af438b6e1-573acdc1-a8b2-4bf0-b433-974da2ac1869-000000%40eu-central-1.amazonses.com.

Reply via email to