360° health check for Django projects — URLs, templates, views auth, forms (blank + POST smoke + orphan cleaned_data), admin, models, security, migrations, DRF serializers, unsafe auto-gen parsing, save(update_fields=...) consistency, FK-redundant columns + HTML reporter + --diff mode — one command
Project description
django-test-doctor
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 toself.fields['fonction'].choicescrashed withAttributeErroron every GET becauseContact.fonctionis a plainCharField— 500 in production. Fixed by theformscheck. - A
post_savesignal onMandatcreated aConfigurationTVAwithout passing the NOT-NULLregimeFK —IntegrityErroron every mandat creation. Fixed by thepost_smokecheck. - An
OperationTVAForm.clean()that assignedcleaned_data["montant_ttc"]whilemontant_ttcwasn't inMeta.fields— Django silently dropped the value, NOT NULL violation at save. Fixed by theforms_metacheck.
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_test_doctor-0.10.4.tar.gz.
File metadata
- Download URL: django_test_doctor-0.10.4.tar.gz
- Upload date:
- Size: 99.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae01e21f18453c90962821cd1313f4b6f1d6ab79d508d2fd9e09bd827c6c8879
|
|
| MD5 |
642a9f8be3f548d814930a1a764e64e4
|
|
| BLAKE2b-256 |
6fe2a2ba962c44086cc61df4887db6dc4f1e1d02a1aa19f0b03486a653972aaa
|
Provenance
The following attestation bundles were made for django_test_doctor-0.10.4.tar.gz:
Publisher:
workflow.yml on Altius-Academy-SNC/django-test-doctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_test_doctor-0.10.4.tar.gz -
Subject digest:
ae01e21f18453c90962821cd1313f4b6f1d6ab79d508d2fd9e09bd827c6c8879 - Sigstore transparency entry: 1351148964
- Sigstore integration time:
-
Permalink:
Altius-Academy-SNC/django-test-doctor@b62833033ef97a44a04c5c8bf38372a956c0d86b -
Branch / Tag:
refs/tags/v0.10.4 - Owner: https://github.com/Altius-Academy-SNC
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@b62833033ef97a44a04c5c8bf38372a956c0d86b -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_test_doctor-0.10.4-py3-none-any.whl.
File metadata
- Download URL: django_test_doctor-0.10.4-py3-none-any.whl
- Upload date:
- Size: 66.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
57e84409777ee61c1ce4f8454320e7416dc8d7d80695484c0f339636d6b118d6
|
|
| MD5 |
66bd33d82e5a4e540fb7cf0924664171
|
|
| BLAKE2b-256 |
26f9b355106692d916db2e9574e0ea2b5873a8c56fa95a20729920b1f6fc648c
|
Provenance
The following attestation bundles were made for django_test_doctor-0.10.4-py3-none-any.whl:
Publisher:
workflow.yml on Altius-Academy-SNC/django-test-doctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_test_doctor-0.10.4-py3-none-any.whl -
Subject digest:
57e84409777ee61c1ce4f8454320e7416dc8d7d80695484c0f339636d6b118d6 - Sigstore transparency entry: 1351149027
- Sigstore integration time:
-
Permalink:
Altius-Academy-SNC/django-test-doctor@b62833033ef97a44a04c5c8bf38372a956c0d86b -
Branch / Tag:
refs/tags/v0.10.4 - Owner: https://github.com/Altius-Academy-SNC
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@b62833033ef97a44a04c5c8bf38372a956c0d86b -
Trigger Event:
release
-
Statement type: