Skip to main content

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

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.fieldsMeta.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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_test_doctor-0.10.4.tar.gz (99.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_test_doctor-0.10.4-py3-none-any.whl (66.1 kB view details)

Uploaded Python 3

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

Hashes for django_test_doctor-0.10.4.tar.gz
Algorithm Hash digest
SHA256 ae01e21f18453c90962821cd1313f4b6f1d6ab79d508d2fd9e09bd827c6c8879
MD5 642a9f8be3f548d814930a1a764e64e4
BLAKE2b-256 6fe2a2ba962c44086cc61df4887db6dc4f1e1d02a1aa19f0b03486a653972aaa

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_test_doctor-0.10.4-py3-none-any.whl.

File metadata

File hashes

Hashes for django_test_doctor-0.10.4-py3-none-any.whl
Algorithm Hash digest
SHA256 57e84409777ee61c1ce4f8454320e7416dc8d7d80695484c0f339636d6b118d6
MD5 66bd33d82e5a4e540fb7cf0924664171
BLAKE2b-256 26f9b355106692d916db2e9574e0ea2b5873a8c56fa95a20729920b1f6fc648c

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page