Feedback wanted: pluggable migration recorder (MIGRATION_RECORDER setting)

Hi all,

I’ve opened a new-feature idea and would love feedback before it goes any further: :backhand_index_pointing_right: Make the migration recorder pluggable via a MIGRATION_RECORDER setting · Issue #180 · django/new-features · GitHub

The short version of the problem

The django_migrations table records that a migration was applied, but not how. A migration applied with migrate --fake / --fake-initial looks identical to one that actually ran its operations. On teams that use faking to reconcile state, this regularly leads to hard-to-debug incidents: the history says a migration is applied, but the schema change never happened. The info even exists at runtime (migrate prints FAKED) — it’s just never persisted, and there’s no supported way for a project to capture it.

Today your only options are monkeypatching MigrationRecorder or subclassing the migrate command, both of which lean on internals that aren’t meant to be overridden.

What I’m proposing

Rather than baking a fake column into the core django_migrations table (which would mean an in-place ALTER on a core table for every existing install, plus committing to one set of semantics), make the recorder itself pluggable:

  • A MIGRATION_RECORDER setting holding a dotted path, defaulting to today’s django.db.migrations.recorder.MigrationRecorder.
  • A small companion change to pass the resolved fake flag into record_applied() so a custom recorder can record how a migration was applied. The base recorder ignores it, so default behavior and the table schema are unchanged.

Projects that want fake-tracking (or audit trails, recording the applying user/host, external/event-based recording, multi-tenant tagging, etc.) then implement a small recorder subclass and own their own schema — Django doesn’t have to.

What I’d like feedback on

  • Does a pluggable recorder feel like the right extension point, or is there appetite for solving the faked-vs-real problem directly in core?
  • Is record_applied(..., fake=...) the right seam, or should the executor hand the recorder richer context (forward/backward, fake-initial vs. explicit fake)?
  • The floating Migration model is currently awkward to subclass (the classproperty + _migration_class caching). Worth refactoring into a cleaner override point as part of this?

Full write-up, alternatives considered, backwards-compatibility notes, and a code sketch are in the issue. If this resonates, :+1:/comments on the GitHub issue help the Steering Council gauge support.

Thanks!

1 Like