## TL;DR
The squash replaced the migration chain but the database history still references the old files, or the squash's final state does not match the real schema. On fresh databases, migrate from zero with the squash. On databases that ran the old migrations, `migrate --fake` the squash to align the history table, then run `makemigrations --check` to prove the state is clean.

```text
django.db.utils.OperationalError: no such column
```

## Steps

1. Find the squash file and read its `replaces` list to see which original migrations it supersedes.
   Expected: the squash names the exact migrations it replaces.

2. Inspect the history table: `select migration, applied from django_migrations where app = '[app]' order by applied;`.
   Expected: rows for the old pre-squash migrations, possibly mixed with the squash itself.

3. On fresh or dev databases, wipe and run `python manage.py migrate` from zero so the squash applies as one unit.
   Expected: migrate completes, and `makemigrations --check --dry-run` reports no changes.

4. On databases that already ran the original migrations, fake-apply the squash so the history table records it without re-running DDL: `python manage.py migrate [app] [squash_name] --fake`.
   Expected: django_migrations shows the squash migration as applied.

5. Verify state everywhere with `python manage.py makemigrations --check --dry-run`.
   Expected: "No changes detected". If it generates a migration, the squash's recorded state is wrong and the squash file needs fixing, not the database.

## Use this when
- OperationalError "no such column" appears right after squashing.
- Old migration rows still sit in django_migrations next to the squash.
- A squash with a replaces list was applied to a database that ran the originals.
- makemigrations wants to generate changes immediately after a squash.

## Not for this skill when
- "No such column" happens on a normal, non-squashed migration.
- The error is a missing table on a fresh database (that is a migrate-ordering problem).
- A model field is simply misspelled (that is an app bug).

## Variant phrasings
- django squash migration no such column
- OperationalError after squash_migrations
- django_migrations out of sync after squash
- replaces list migration state wrong

## Why it happens
Squashing merges several migrations into one file with a `replaces` list. If the squash's final model state does not exactly match the real schema (a field added later without regenerating the squash), or the history table still points at the old files, Django applies or skips the wrong sequence and queries hit columns that do not exist.

## Edge cases
- Squashes containing data migrations need the RunPython marked elidable, or the data step re-runs on every fresh migrate.
- Circular replaces across apps require both apps to be squashed consistently.
- Keep the old migration files until every environment has migrated past the squash. Deleting them early strands databases that still reference them.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_t0FXv0QliqW69a-1vsJaig
