Skip to main content

django-test-doctor

PyPI version Checks Tests Python Django License: MIT Docs

360° health check for Django projects. One command, fifteen layers of analysis, one verdict. Catches crashes your unit tests miss because they never exercise the blank-init / GET / POST / signal-save / {% url %} / permission-guard / auto-gen-parse / update_fields-consistency / fk-redundancy pathway an actual user hits first.

Installed as django-test-doctor, imported as django_doctor:

pip install django-test-doctor
./manage.py doctor

Why

./manage.py check validates runtime configuration. djlint validates template syntax. pytest validates whatever you remembered to test. Nothing validates the whole project at once. Doctor does.

Real-world bugs doctor caught in the past week:

  • A form whose __init__ assigns to self.fields['fonction'].choices crashed with AttributeError on every GET because Contact.fonction is a plain CharField — 500 in production. Fixed by the forms check.
  • A post_save signal on Mandat created a ConfigurationTVA without passing the NOT-NULL regime FK — IntegrityError on every mandat creation. Fixed by the post_smoke check.
  • An OperationTVAForm.clean() that assigned cleaned_data["montant_ttc"] while montant_ttc wasn't in Meta.fields — Django silently dropped the value, NOT NULL violation at save. Fixed by the forms_meta check.

Every one of these passed ./manage.py check, passed its unit test suite, and still reached production.

Layers

Layer What it does Status
system Wraps Django's built-in check framework ✅ 0.1
urls Crawls urlpatterns, probes every URL as anon / user / staff / superuser ✅ 0.1
forms Instantiates every Form / ModelForm blank + with empty data ✅ 0.1
migrations Byte-for-byte parity with makemigrations --check --dry-run ✅ 0.1
admin list_display / list_filter / search_fields / ordering exist on the model ✅ 0.2
models __str__ defined, Meta.ordering, no SET_NULL on NOT NULL FK ✅ 0.2
security check --deploy + weak SECRET_KEY + DEBUG=True + empty ALLOWED_HOSTS ✅ 0.2
drf Serializer init + Meta.fields ↔ Meta.model coherence ✅ 0.3
post_smoke POST every CreateView / UpdateView with auto fixture, fail on 5xx ✅ 0.4
forms_meta cleaned_data[x] = … where x is NOT NULL but not in Meta.fields ✅ 0.5
templates Broken {% url %} / {% static %} / template syntax in first-party templates ✅ 0.6
views First-party view with no LoginRequired / @login_required / DRF permission ✅ 0.6
autogen int(x.split('-')[-1]) auto-gen in Model.save() without try/except ✅ 0.7
update_fields obj.save(update_fields=[...]) dropping fields that save() recomputes ✅ 0.8
fk_redundant Column duplicating a value reachable via a ForeignKey on the same model ✅ 0.10
i18n Untranslated msgid, stale .mo vs .po, missing _() wrapping 🚧 0.11

Plus in 0.6: --html <path> (standalone HTML report suitable for CI artifacts), --diff <ref> (only show findings on files changed since a git ref — perfect for PR-scoped gating), and a URL-kwargs fixture generator that unlocks the urls and post_smoke checks on detail and edit routes (<int:pk>, <uuid:id>, <slug:slug>).

Quickstart

pip install django-test-doctor

Add to INSTALLED_APPS:

INSTALLED_APPS = [
    ...,
    "django_doctor",
]

Then:

./manage.py doctor                              # full report
./manage.py doctor --section forms,post_smoke   # target specific layers
./manage.py doctor --quick                      # skip slow layers
./manage.py doctor --ci                         # exit 1 on critical/error
./manage.py doctor --json                       # machine-readable output

Safety

post_smoke writes to the database (creates a transient superuser, submits forms) but always rolls back via transaction.atomic() + savepoint_rollback. Still, point it at a dev or CI database — never production.

Configuration

In your project's pyproject.toml:

[tool.django-doctor]
enabled = ["*"]
disabled = []                          # or ["post_smoke"] to skip slow layers
fail_on = ["critical", "error"]        # what --ci should bail on
ignore = ["urls:admin:*", "forms:legacy_migrations.*"]

[tool.django-doctor.urls]
roles = ["anonymous", "authenticated", "staff", "superuser"]
skip  = ["admin:*", "djdt:*", "rp-initiated-logout"]
timeout = 10

[tool.django-doctor.forms]
scenarios = ["blank", "empty_data"]

[tool.django-doctor.migrations]
skip_apps = []
include_third_party = false

[tool.django-doctor.post_smoke]
skip = ["legacy:*"]

[tool.django-doctor.security]
forbidden_secret_keys = ["django-insecure", "changeme", "secret"]

Plugin API

Third-party checks plug in via the django_doctor.checks entry point:

[project.entry-points."django_doctor.checks"]
my_check = "my_package.checks:MyCheck"
# my_package/checks.py
# Distribution is django-test-doctor, import path is django_doctor.
from django_doctor import Check, Finding, Severity

class MyCheck(Check):
    id = "my_check"
    description = "Verify my custom invariant"

    def run(self, project):
        for thing in project.iter_something():
            if not thing.ok:
                yield Finding(
                    severity=Severity.ERROR,
                    location=thing.file_path,
                    message=f"{thing!r} is broken",
                    fix_hint="Run ./manage.py fix_things",
                )

Need auto-generated form fixtures in your own tests? The helper doctor uses internally is public:

from django_doctor.fixtures import make_form_fixture

data = make_form_fixture(MyModelForm)
client.post("/my-view/", data)

Status

Alpha — 0.10 ships fifteen layers + HTML reporter + --diff mode + URL-kwargs fixtures. Roadmap in CHANGELOG.md. Battle-tested against a production Django 6 / DRF / PostGIS / pgvector codebase with ~175 models, ~150 forms, 202 serializers, and 300+ URLs.

License

MIT © Paul Guindo

Release files for django-test-doctor 0.10.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-test-doctor 0.10.4
File Size Uploaded
django_test_doctor-0.10.4.tar.gz 99.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-test-doctor 0.10.4
File Interpreter ABI Platform
django_test_doctor-0.10.4-py3-none-any.whl Python 3 none any Details

Total release size: 165.2 kB

Release files / django_test_doctor-0.10.4.tar.gz

Download URL django_test_doctor-0.10.4.tar.gz
Size 99.1 kB
Tags Source
SHA-256 checksum
How to use checksums
ae01e21f18453c90962821cd1313f4b6f1d6ab79d508d2fd9e09bd827c6c8879
BLAKE2b-256 checksum
How to use checksums
6fe2a2ba962c44086cc61df4887db6dc4f1e1d02a1aa19f0b03486a653972aaa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 21, 2026.

Transparency log

Release files / django_test_doctor-0.10.4-py3-none-any.whl

Download URL django_test_doctor-0.10.4-py3-none-any.whl
Size 66.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
57e84409777ee61c1ce4f8454320e7416dc8d7d80695484c0f339636d6b118d6
BLAKE2b-256 checksum
How to use checksums
26f9b355106692d916db2e9574e0ea2b5873a8c56fa95a20729920b1f6fc648c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 21, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.10.4 This release

2 release files

0.10.3

2 release files

0.10.2

2 release files

0.10.1

2 release files

0.10.0

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page