#36355: sqlmigrate gives unexpected output for an AlterField on a PK when 
another
app has a model with a FK
--------------------------+--------------------------------------
     Reporter:  Tim Bell  |                     Type:  Bug
       Status:  new       |                Component:  Migrations
      Version:  5.2       |                 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've recently discovered a scenario where the output of `sqlmigrate` for a
 particular migration is significantly different from the actual SQL run
 when the migration is applied. (I am following the advice
 [https://docs.djangoproject.com/en/5.2/ref/django-admin/#django-admin-
 sqlmigrate here] to run `sqlmigrate` "against a copy of the database you
 wish to later apply it on".) This occurs when a primary key field is being
 altered when there is a foreign key field on a model in another app that
 refers to that primary key field.

 Consider an app "myapp" with this model:

 {{{
 class MyModel(models.Model):
     id = models.AutoField(primary_key=True)
 }}}

 And another app "otherapp" with this model:

 {{{
 from myapp.models import MyModel

 class OtherModelInOtherApp(models.Model):
     my_model = models.ForeignKey(MyModel, on_delete=models.CASCADE)
 }}}

 Run `makemigrations` and `migrate` to apply those initial migrations. (A
 sample repo containing this scenario is
 [https://github.com/timb07/sqlmigrate_bug here]. The
 
[https://github.com/timb07/sqlmigrate_bug/commit/e3002b11d02c7526408d2cceb174f60d66a54eb3
 initial commit] represents this state.)

 Then change the `id` field of `MyModel` to `BigAutoField`, and run
 `makemigrations` again.
 
([https://github.com/timb07/sqlmigrate_bug/commit/d9106ad1b691392fc6f12aa2d19c5bce8f059910
 This commit] represents this state.)

 Now run `sqlmigrate myapp 0002`, giving this output (using Postgresql):

 {{{
 BEGIN;
 --
 -- Alter field id on mymodel
 --
 ALTER TABLE "myapp_mymodel" ALTER COLUMN "id" TYPE bigint USING
 "id"::bigint;
 ALTER SEQUENCE IF EXISTS "myapp_mymodel_id_seq" AS bigint;
 COMMIT;
 }}}

 There is no mention of the `otherapp_othermodelinotherapp`, its column
 `my_model_id`, or the foreign key constraint referencing
 `myapp_mymodel.id`. In contrast, when the migration is applied, the
 existing FK constraint is dropped, the `my_model_id` is altered to bigint,
 and a new FK constraint is created.

 My understanding is that the because there is no dependency from the
 `myapp 0002_alter_mymodel_id` migration on the `otherapp 0001_initial`
 migration, `sqlmigrate` does not consider the latter migration when
 determining the state of the models. That means that it doesn't know that
 there is a foreign key relationship that will be affected by the
 `AlterField` migration operation.

 Indeed, adding a dependency in `otherapp/migrations/0001_initial.py` like
 this:

 {{{
     run_before = [
         ('myapp', '0002_alter_mymodel_id'),
     ]
 }}}

 results in the output of `sqlmigrate` being as expected now:

 {{{
 BEGIN;
 --
 -- Alter field id on mymodel
 --
 SET CONSTRAINTS "otherapp_othermodeli_my_model_id_cc392f6b_fk_myapp_mym"
 IMMEDIATE; ALTER TABLE "otherapp_othermodelinotherapp" DROP CONSTRAINT
 "otherapp_othermodeli_my_model_id_cc392f6b_fk_myapp_mym";
 ALTER TABLE "myapp_mymodel" ALTER COLUMN "id" TYPE bigint USING
 "id"::bigint;
 ALTER SEQUENCE IF EXISTS "myapp_mymodel_id_seq" AS bigint;
 ALTER TABLE "otherapp_othermodelinotherapp" ALTER COLUMN "my_model_id"
 TYPE bigint USING "my_model_id"::bigint;
 ALTER TABLE "otherapp_othermodelinotherapp" ADD CONSTRAINT
 "otherapp_othermodelinotherapp_my_model_id_cc392f6b_fk" FOREIGN KEY
 ("my_model_id") REFERENCES "myapp_mymodel" ("id") DEFERRABLE INITIALLY
 DEFERRED;
 COMMIT;
 }}}

 This behaviour has been observed in various Django 5.x and 4.x versions; I
 suspect it has always been present since migrations were added to Django.

 It would be good if this behaviour was noted in the documentation for
 `sqlmigrate`. Ideally though, `sqlmigrate` should reflect the current
 state of applied migrations in the database, as it already does to resolve
 constraint names.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36355>
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/0107019677c62640-83fd2438-0da8-49de-b51e-66346c360a3a-000000%40eu-central-1.amazonses.com.

Reply via email to