Skip to content

Core API

Public classes re-exported from django_doctor.

Check

django_doctor.registry.Check

Base class for every analyzer.

Source code in src/django_doctor/registry.py
class Check:
    """Base class for every analyzer."""

    id: str = ""  # unique, lowercase (e.g. "urls", "forms")
    description: str = ""
    # Higher = runs later. Cheap/critical checks should be low (system=10,
    # urls=50, forms=40). UI checks that rely on others come later.
    order: int = 100
    # Set to True for checks that hit external services or take > 5s locally.
    slow: bool = False

    def run(self, project: Project) -> Iterable[Finding]:
        raise NotImplementedError

    def get_config(self, project: Project) -> dict:
        return project.config.get(self.id, {})

Finding

django_doctor.finding.Finding dataclass

One actionable item produced by a check.

Keep Findings small and self-contained — they're the unit the reporter renders, the JSON exporter serializes, and the ignore-rules match against.

Source code in src/django_doctor/finding.py
@dataclass(slots=True)
class Finding:
    """One actionable item produced by a check.

    Keep Findings small and self-contained — they're the unit the reporter
    renders, the JSON exporter serializes, and the ignore-rules match against.
    """

    severity: Severity
    check_id: str
    message: str
    rule: str = ""  # sub-rule id inside a check, e.g. "forms.blank_init"
    location: str = ""  # "path/to/file.py:42" or a URL name
    fix_hint: str = ""
    extra: dict[str, Any] = field(default_factory=dict)

    @property
    def ignore_key(self) -> str:
        """Stable key used to silence a finding via config (ignore list)."""
        if self.rule:
            return f"{self.check_id}:{self.rule}"
        return f"{self.check_id}:*"

ignore_key property

Stable key used to silence a finding via config (ignore list).

Severity

django_doctor.finding.Severity

Bases: IntEnum

Ordered so comparisons like severity >= Severity.ERROR work.

Source code in src/django_doctor/finding.py
class Severity(IntEnum):
    """Ordered so comparisons like `severity >= Severity.ERROR` work."""

    INFO = 10
    WARNING = 20
    ERROR = 30
    CRITICAL = 40

    @property
    def label(self) -> str:
        return self.name.lower()

    @property
    def emoji(self) -> str:
        return {
            Severity.INFO: "ℹ",
            Severity.WARNING: "⚠",
            Severity.ERROR: "✗",
            Severity.CRITICAL: "☠",
        }[self]

    @classmethod
    def from_string(cls, value: str) -> "Severity":
        return cls[value.upper()]

Project

django_doctor.registry.Project dataclass

Handle passed to each check — exposes the Django project under audit.

Kept deliberately small so checks don't grow tangled dependencies on internals. Extend with lazy properties (@cached_property) rather than eager imports so each check only pays for what it uses.

Source code in src/django_doctor/registry.py
@dataclass
class Project:
    """Handle passed to each check — exposes the Django project under audit.

    Kept deliberately small so checks don't grow tangled dependencies on
    internals. Extend with lazy properties (`@cached_property`) rather than
    eager imports so each check only pays for what it uses.
    """

    base_dir: str = ""
    config: dict = field(default_factory=dict)