Skip to content

fix(migrations): make the ui_use_tailwind removal idempotent - #15842

Merged
Maffooch merged 1 commit into
devfrom
cmm/idempotent-drop-ui-use-tailwind
Sep 1, 2026
Merged

fix(migrations): make the ui_use_tailwind removal idempotent#15842
Maffooch merged 1 commit into
devfrom
cmm/idempotent-drop-ui-use-tailwind

Conversation

@Maffooch

@Maffooch Maffooch commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Shortcut: sc-14995

Description

0293_remove_usercontactinfo_ui_use_tailwind removes the ui_use_tailwind opt-in (added in 0267) with a plain RemoveField. That emits an unconditional ALTER TABLE dojo_usercontactinfo DROP COLUMN ui_use_tailwind, which fails when the column is not physically present at the moment the migration runs:

django.db.utils.ProgrammingError: column "ui_use_tailwind" of relation "dojo_usercontactinfo" does not exist

This is reachable on the upgrade path — a database can arrive at 0293 without the column present when its recorded migration history skews from its physical schema (e.g. the column addition never materialized in that DB even though later history is applied). A plain RemoveField cannot tolerate that, and the whole migrate run aborts.

Fix

Split the operation into SeparateDatabaseAndState:

  • state keeps the RemoveField, so Django's model state is unchanged — no new migration is generated, and the model no longer defines the field.
  • database does an idempotent ALTER TABLE ... DROP COLUMN IF EXISTS, so the drop is a no-op when the column is already gone instead of an error.

UserContactInfo is not pghistory-tracked, so no event table carries the column and a raw ALTER TABLE is sufficient. This mirrors the existing SeparateDatabaseAndState removal pattern in 0266_remove_credential_manager. The reverse re-adds the column with its original 0267 definition (BooleanField(default=False)) so a downgrade restores a working schema.

Validation

  • Reproduced the exact failure against Postgres: a plain ALTER TABLE ... DROP COLUMN on an absent column raises column "ui_use_tailwind" ... does not exist, while DROP COLUMN IF EXISTS emits a skip NOTICE and succeeds.
  • state_operations is an identical RemoveField, so the model-state result is unchanged (no makemigrations drift).
  • The module lives under dojo/db_migrations, which is excluded from the ruff lint config.

0293_remove_usercontactinfo_ui_use_tailwind used a plain RemoveField, which
emits an unconditional `ALTER TABLE ... DROP COLUMN` and fails with
`column "ui_use_tailwind" of relation "dojo_usercontactinfo" does not exist`
whenever the column is not physically present when the migration runs — which
the OSS->Pro upgrade path hits (the upgrade fixture reaches 0293 without the
column), and which any instance with migration-history/schema skew can hit too.

Split the operation into SeparateDatabaseAndState: keep the state RemoveField so
Django's model state is unchanged (no new migration is generated), and make the
database drop idempotent with `DROP COLUMN IF EXISTS`. UserContactInfo is not
pghistory-tracked, so no event table carries the column and a raw ALTER TABLE is
sufficient. Mirrors the existing SeparateDatabaseAndState removal pattern in
0266_remove_credential_manager. The reverse re-adds the column with its original
0267 definition.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the New Migration Adding a new migration file. Take care when merging. label Sep 1, 2026
@Maffooch Maffooch added this to the 3.3.0 milestone Sep 1, 2026
@Maffooch
Maffooch merged commit 78a16dd into dev Sep 1, 2026
70 of 73 checks passed
@Maffooch
Maffooch deleted the cmm/idempotent-drop-ui-use-tailwind branch September 1, 2026 00:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

New Migration Adding a new migration file. Take care when merging.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants