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 Adam Johnson)

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:

  1. PostgreSQL (tested on 15 and reproduced on 18)
  2. A model that has been migrated from vanilla to use multi-table inheritance
  3. 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 Adam Johnson, 15 hours ago

Description: modified (diff)

comment:3 by Adam Johnson, 13 hours ago

Resolution: wontfix
Status: assignedclosed

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.

Note: See TracTickets for help on using tickets.
Back to Top