Skip to content

models & migrations

Two data-layer hygiene checks. models is pure introspection; migrations delegates to ./manage.py makemigrations --check --dry-run for byte-for-byte parity with the CLI verdict.

models

For every first-party, non-abstract model, checks:

  1. __str__ — if Model.__str__ is models.Model.__str__ (i.e. not overridden), emit WARNING. The default repr (<MyModel: object (42)>) makes admin / shell / logs painful.
  2. Meta.ordering — absent → INFO (WARNING if listed in soft_checks). Pagination becomes DB-order-dependent.
  3. on_delete=SET_NULL on a NOT NULL FK — ERROR. Silent time-bomb: works until the first parent deletion, then IntegrityError.

Tuning

[tool.django-doctor.models]
skip_apps  = ["admin", "auth", "contenttypes", "sessions", "messages", "sites"]
soft_checks = ["ordering_missing"]   # emit WARNING instead of INFO

migrations

Calls call_command("makemigrations", dry_run=True, check_changes=False) in-process, captures stdout, and parses Migrations for 'app': lines. Byte-for-byte parity with ./manage.py makemigrations --check --dry-run.

Why not reimplement MigrationAutodetector directly? Because the makemigrations command carries subtle defaults (the questioner, app trimming, consistency checks) that diverge from a raw autodetector call. Earlier releases did — 0.2.1 reported phantom AlterField drift on up-to-date projects. 0.2.2 fixed it by delegating.

Also reports migrations.unapplied when the migration graph has leaves that aren't applied to DEFAULT_DB_ALIAS.

Why only first-party apps

By default migrations skips everything whose app source lives in site-packages/ or dist-packages/. Users can't fix drift in third-party packages anyway — it's noise. Flip to reports-everything with:

[tool.django-doctor.migrations]
include_third_party = true