django-stubs

PEP 484 type stubs and a custom mypy plugin that bring precise static typing to Django's dynamic ORM and framework internals.

Library
PyPI
v6.1.0
1,973stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
92/100Excellent
Development Activity100
Maintenance96
Community72
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
80/100Excellent
Architecture85
Code Quality90
Innovation80
Learning Curve65

django-stubs contains type stubs and a custom mypy plugin that give Django projects precise static type checking. Django relies on runtime “magic” — dynamically generated manager attributes, metaclass-driven model fields, settings resolved from arbitrary modules — that plain type stubs can’t express on their own. The mypy plugin reads an app’s Django settings and model registry at analysis time to fill in the gaps: it infers queryset/manager generics, generates reverse relation accessors, validates Meta class attributes, and resolves lazy translation types.

The project ships as two installable pieces: django-stubs (the stubs plus the mypy plugin) and django-stubs-ext (a small runtime helper package that monkeypatches Django’s generic classes so QuerySet[MyModel]-style annotations work outside of type-checking, and that can be safely imported at runtime). Both are maintained together in this monorepo alongside a pytest-mypy-plugins-based test suite that runs real mypy analysis against hundreds of Django code samples.

Basic support for pyright, pyrefly, and ty is also checked in CI, though the mypy plugin remains the primary integration path and the one with full Django-specific type inference. The project tracks new Django and mypy releases closely, with an explicit compatibility matrix in the README mapping each django-stubs release to the Django and mypy versions it supports.

What You Get

  • Full Django type stubs - PEP 484 stubs covering models, forms, views, middleware, URLs, templates, and the rest of Django’s public API.
  • A custom mypy plugin - reads your app registry and django_settings_module at analysis time to type model managers, querysets, and reverse relations accurately.
  • django-stubs-ext runtime package - a companion package with monkeypatch() to make Django’s generic classes (QuerySet[Model], Manager[Model]) subscriptable at runtime, plus TypedModelMeta and WithAnnotations helpers.
  • Configurable strictness settings - strict_settings and strict_model_abstract_attrs let you dial down type strictness for dynamic settings modules or abstract base models.
  • Basic pyright/pyrefly/ty support - stub coverage is checked against three additional type checkers in CI, for teams not using mypy.

Common Use Cases

  • Catching ORM misuse before runtime - a team enables the mypy plugin in CI so that incorrect field types, bad manager usage, or wrong queryset chaining fail the build instead of production.
  • Typing custom managers and querysets - a developer subclasses models.Manager[MyModel] and models.QuerySet[MyModel] and gets accurate return types on create(), get(), and chained queryset methods.
  • Working with reverse relations safely - a codebase with many ForeignKey and ManyToManyField relations uses the plugin’s auto-generated RelatedManager/ManyRelatedManager types instead of hand-writing them.
  • Migrating a large Django codebase to typed Python - an engineering team incrementally adds mypy to an existing Django app, using django-stubs-ext.monkeypatch() so generic model types work at runtime during the transition.
  • Annotating queryset .annotate() results - a project uses WithAnnotations[Model, TypedDict] to give computed/annotated fields proper types instead of losing type information after .annotate() calls.

Under The Hood

Architecture django-stubs is organized as two cooperating pieces: a large tree of .pyi stub files under django-stubs/ that mirror Django’s own package layout (db, forms, http, views, urls, etc.), and mypy_django_plugin/, a mypy Plugin subclass (NewSemanalDjangoPlugin in main.py) that hooks into mypy’s semantic analysis pipeline via get_additional_deps, attribute/method/function hooks, and dynamic class-def hooks. The plugin builds a DjangoContext from the project’s django_settings_module, which lets transformers/ modules (models.py, managers.py, querysets.py, forms.py, auth.py, settings.py, and others) resolve real app config, model fields, and settings values at analysis time rather than relying on static stub declarations alone. This is the core design tension the project manages: Django’s runtime dynamism versus mypy’s static analysis model, bridged by re-running a subset of Django’s own registration logic inside the plugin.

Tech Stack The project is pure Python (99.8% by the GitHub language breakdown), targeting Python 3.11+ and built with uv_build in a uv workspace that also contains the ext sub-package (django-stubs-ext). Core dependencies are django itself, typing-extensions, and types-pyyaml; optional dependency groups add support for Oracle (oracledb) and Redis (redis, types-redis) backends. Static analysis tooling is unusually broad for a stubs package: mypy (the primary target), pyright, pyrefly, and ty are all configured and checked in CI, each with its own dependency group and settings block in pyproject.toml.

Code Quality Testing relies on pytest-mypy-plugins, which runs real mypy analysis against YAML-defined test cases under tests/ (organized by Django subsystem: models, managers, fields, forms, views, contrib) and compares the resulting diagnostics against expected output — a pattern well suited to a project whose actual product is type-checker behavior. tests/assert_type/ additionally exercises the stubs directly against pyright/pyrefly/ty in strict mode. Linting is comprehensive: ruff with an extensive rule selection (bugbear, pyflakes, pyupgrade, flake8-type-checking, flake8-pyi, and more) plus codespell, and CI (test.yml) runs the full suite on each push. strict_type_ignore conventions are enforced by disabling generic # type: ignore in favor of tool-specific ignore comments, catching stale suppressions.

What Makes It Unique Unlike most typeshed-style stub packages, django-stubs pairs its stubs with a mypy plugin that performs live introspection of the consuming project’s Django app registry and settings module during type checking — effectively re-deriving parts of Django’s own dynamic behavior (reverse relation names, Meta attribute validation, manager generics) inside the type checker rather than trying to express it all statically. Checking basic compatibility against three additional type checkers (pyright, pyrefly, ty) in the same CI run, despite none of them supporting a plugin hook, is also uncommon discipline for a project this specialized.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search