Opened 15 hours ago
Closed 13 hours ago
#36645 closed Bug (wontfix)
PostgreSQL migration regression: `InternalError: cannot drop column id of table ... because other objects depend on it`
Reported by: | Adam Johnson | Owned by: | Adam Johnson |
---|---|---|---|
Component: | Migrations | Version: | 6.0 |
Severity: | Release blocker | 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 )
When testing a client project on Django 6.0, its migrations fail to run, crashing with an exception like:
django.db.utils.InternalError: cannot drop column id of table library_book because other objects depend on it DETAIL: constraint ratings_rating_book_id_9d017958_fk_library_book_id on table ratings_rating depends on column id of table library_book HINT: Use DROP ... CASCADE to drop the dependent objects too.
The exact conditions:
- PostgreSQL (tested on 15 and reproduced on 18)
- A model that has been migrated from vanilla to use multi-table inheritance
- A model in another app that FK's to the first model, with its migration dependency on the first models' first migration.
Reproduction repository: https://github.com/adamchainz/django-ticket-36645
Change History (3)
comment:1 by , 15 hours ago
Description: | modified (diff) |
---|
comment:2 by , 15 hours ago
comment:3 by , 13 hours ago
Resolution: | → wontfix |
---|---|
Status: | assigned → closed |
Aha, this is indeed a feature, not a bug.
It turns out that the migrations on Django 5.2 would drop the foreign key constraint, due to the cascade. Now PostgreSQL doesn't allow that, preserving data integrity.
With my reproducer example, if I check the table *before* running the final migration, I see:
# \d+ ratings_rating Table "public.ratings_rating" Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+--------+-----------+----------+----------------------------------+---------+-------------+--------------+------------- id | bigint | | not null | generated by default as identity | plain | | | book_id | bigint | | not null | | plain | | | Indexes: "ratings_rating_pkey" PRIMARY KEY, btree (id) "ratings_rating_book_id_9d017958" btree (book_id) Foreign-key constraints: "ratings_rating_book_id_9d017958_fk_library_book_id" FOREIGN KEY (book_id) REFERENCES library_book(id) DEFERRABLE INITIALLY DEFERRED Access method: heap
Then if I run the migration:
$ ./manage.py migrate Operations to perform: Apply all migrations: library, ratings Running migrations: Applying library.0002_readable_remove_book_id_book_readable_ptr... OK
…then, boom! The foreign key constraint is gone:
# \d+ ratings_rating Table "public.ratings_rating" Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description ---------+--------+-----------+----------+----------------------------------+---------+-------------+--------------+------------- id | bigint | | not null | generated by default as identity | plain | | | book_id | bigint | | not null | | plain | | | Indexes: "ratings_rating_pkey" PRIMARY KEY, btree (id) "ratings_rating_book_id_9d017958" btree (book_id) Access method: heap
Moreover, I've run the same verification on my client's production app and found missing foreign key constraints. Time to roll the sleeves up and get to re-adding them…
So I think we can mark this as "working as intended".
One other note: I found that the migration pattern didn't work on SQLite with Django 5.2. That makes sense because it never used CASCADE
.
Bisects to 2a5aca38bbb37f6e7590ac6e68912bfbefb17dae.