django-stubs: Precise Typing for Django with mypy
PEP-484 stubs for Django
At a glance
- What is it?
- django-stubs adds PEP 484 stubs and a custom mypy plugin for Django projects where framework behavior and dynamic Python patterns make ordinary type inference incomplete. It offers its deepest support with mypy, while pyright, pyrefly, and ty receive only basic support.
- Who is it for?
- Adopt django-stubs when a Python team uses Django with mypy and wants stricter, framework-aware checking. Do not assume its pyright, pyrefly, or ty support has the same depth, and first verify the selected django-stubs release against the project's Django, mypy, Python, and operating system dependencies.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why Django Needs a Dedicated Typing Layer
django-stubs addresses a specific gap between Django's runtime behavior and what a static type checker can infer from ordinary Python. Django relies on dynamic patterns, including model metadata, managers, settings, database routers, and. A checker that sees only source code and annotations may miss or misdescribe those elements.
The package supplies two related pieces. Its stub package gives type checkers descriptions of Django APIs, while `mypy_django_plugin.main` participates in mypy analysis and supplies more precise types and inference for common Django patterns. This is most useful to teams that want errors found before deployment without replacing Django or adding runtime annotations throughout the application.
It is aimed at Python projects already using static checking, especially Django applications run with mypy. The package is also checked with pyright, pyrefly, and ty, but the README labels those integrations as basic support. For pyright, pyrefly, or ty, the stubs may still help, yet the plugin's advanced Django-aware behavior is documented only for mypy.
The project does not change the runtime type of a Django model or replace Django's ORM. Its effect occurs while a checker examines the project. That distinction matters when setting expectations: django-stubs can expose unsupported model attributes, invalid query expressions, and mismatched Django API usage, but it cannot guarantee that a business operation is correct.
How the Stub Package and mypy Plugin Cooperate
The stub package resolves the type signatures that Django itself does not expose, while the plugin handles cases where those signatures alone are insufficient. According to the repository layout, the distribution includes the `django-stubs/` stub tree, the `mypy_django_plugin/` implementation, and the `ext/` package that supplies typed Django extension bases.
Plugin loading is explicit. Mypy needs `mypy_django_plugin.main` listed under its plugin configuration. The plugin also needs to locate the Django settings module, either through `django_settings_module` or through the `DJANGO_SETTINGS_MODULE` environment variable. Once configured, Django-specific analysis can account for a codebase's actual settings rather than treating settings access as an isolated import.
The extension package adds stricter base classes where useful. With the mypy plugin and `django_stub_ext` installed, model `Meta` classes are checked automatically. Developers who want an explicit declaration inside a model can inherit from `TypedModelMeta`, which gives attributes such as `ordering` and `constraints` their expected types. The package also provides `TypedDatabaseRouter` for custom database router implementations.
Settings behavior is deliberately strict by default. `strict_settings` defaults to `true`, while `strict_model_abstract_attrs` also defaults to `true`. These defaults constrain assumptions that can conceal incorrect use of dynamic settings or abstract-model attributes. Disabling them is possible, but it changes what the checker knows and belongs only in projects that understand the documented reason.
Installing django-stubs and Connecting Django Settings
For a mypy-based project, install the package through the `compatible-mypy` extra. The README gives this exact command:
pip install 'django-stubs[compatible-mypy]'The extra installs a bounded mypy dependency, with the current project metadata specifying `mypy>=1.13,<2.5`. A `pyproject.toml` configuration can register both the plugin and the settings module:
[tool.mypy]
plugins = ["mypy_django_plugin.main"]
[tool.django-stubs]
django_settings_module = "myproject.settings"Replace `myproject.settings` with the actual import path used by the Django project. A reader configuring this for the first time should confirm that mypy loads `mypy_django_plugin.main` and that the settings module can be resolved. Without an explicit `django_settings_module`, django-stubs reads `DJANGO_SETTINGS_MODULE` from the environment.
Projects using `mypy.ini` or `setup.cfg` can use the equivalent sections documented by the README:
[mypy]
plugins =
mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "myproject.settings"The project itself uses `uv` and exposes mypy recipes through `justfile`. For repository development, its documented check invokes mypy in the synchronized environment:
uv run mypy ext scripts mypy_django_pluginThat command targets django-stubs' own extension, script, and plugin code rather than an arbitrary Django application. A consuming project should run mypy through its own established check command after configuration.
Typing Model Meta Attributes in a Real Model
A first application-level change can be made in an existing Django model by using `TypedModelMeta`. The following example is taken from the README and adds stricter checking to model options:
from django.db import models
from django_stubs_ext.db.models import TypedModelMeta
class MyModel(models.Model):
example = models.CharField(max_length=100)
class Meta(TypedModelMeta):
ordering = ["example"]
constraints = [
models.UniqueConstraint(fields=["example"], name="unique_example"),
]The field declaration remains ordinary Django code. The relevant change is the `Meta` base class, which tells the type system that this metadata block should follow the expected model option types. After the package, extension package, and plugin are installed, the model's metadata can participate in stricter analysis.
The README states that `Meta` classes are checked automatically when the mypy plugin and `django_stub_ext` are present. Explicitly inheriting from `TypedModelMeta` still gives the declaration a visible, local contract and is useful when adopting django-stubs incrementally across an existing model layer.
The configuration in the preceding section must point at the project's settings module. Otherwise, Django-aware analysis may not have the application context needed to interpret model behavior correctly. The first useful verification is therefore not merely that mypy starts, but that the plugin loads and existing models continue to be analyzed under the intended settings.
Version Compatibility and Upgrade Boundaries
django-stubs is tied to the behavior of particular Django and checker releases. Its compatibility table is not an open-ended promise for every Python, Django, and mypy combination. Version 6.1.2, which matches the current project metadata, supports Python 3.11 through 3.14, mypy 1.13 through 2.4, and Django 6.1, with partial support listed for Django 6.0 and 5.2.
That distinction between full and partial support is important. “Partial” does not have a single interchangeable meaning for every API. The README points to a separate discussion explaining why the project does not pin only one exact Django and mypy version. In practical terms, a release can recognize several versions while some Django or checker behavior remains outside its strongest support.
The repository's recent releases show repeated compatibility work across the 6.1 line. Versions 6.1.0, 6.1.1, and 6.1.2 were published on 2026-08-12, 2026-09-14, and 2026-10-05 respectively. The last push was on 2026-10-14. That is evidence of recent repository activity, but it does not remove the need to compare the compatibility table before upgrading either dependency.
Upgrade cost is highest when a project changes Django, Python, and mypy together. Since django-stubs can report previously hidden type errors, a newly enabled plugin may expose issues in application code as well as configuration problems. Teams should treat the release table and a clean mypy run on the chosen dependency set as acceptance criteria.
The package uses the MIT license. The project metadata identifies `LICENSE.md` as its license file. That information describes the distribution's licensing terms, but this review does not interpret how a particular organization's policies apply to it.
Where django-stubs Can Misjudge or Become the Wrong Tool
The clearest limitation is checker depth. Mypy has full and complete support with multiple advanced plugin features, according to the README. Pyright, pyrefly, and ty have basic support only. A team standardized on one of those checkers may receive useful stub information without getting the same Django-specific plugin behavior, so a clean result should not be interpreted as equivalence with mypy.
The package also cannot make every dynamically generated Django expression inferable. The project identifies Django's use of Python “magic” as the reason the stubs and plugin exist, and calls improved inference for common patterns the final goal. That wording leaves room for uncommon or heavily dynamic code to require explicit annotations, `# type: ignore` handling, or exclusion from checked paths.
Strict settings checking is another boundary. Projects that construct settings dynamically may need `strict_settings = false`. Disabling it admits code patterns that do not provide the same level of information to the checker. Similarly, `strict_model_abstract_attrs` controls whether manager and exception attributes remain directly present on `models.Model`. Keeping them by default is safer because an attribute may not exist on the actual model class.
For runtime-heavy frameworks, IDE refactoring, or applications whose core risk lies outside static analysis, django-stubs may produce limited benefit. It is also unnecessary for a Django codebase that does not use a supported type checker. The adoption case depends on whether the team intends to act on mypy findings.
django-stubs Compared with Runtime Typing Approaches
A real alternative is to annotate Django-facing application boundaries directly in Python source. Instead of adding django-stubs, a team can type its own views, forms, query helpers, managers, and model methods with standard annotations. That approach can work without a Django-aware plugin and does not require an additional stub distribution. Its disadvantage is that annotation work falls to the application team, and dynamic Django behavior may remain ambiguous.
django-stubs uses a different approach. It describes Django itself as a typed package and adds plugin hooks that let mypy inspect framework-specific structure. The project does not require application developers to rewrite every model or manager as typed abstractions. `TypedModelMeta` and `TypedDatabaseRouter` provide selected local contracts, while the plugin carries much of the Django-aware analysis.
Another alternative is choosing a checker with built-in Django knowledge, though the repository documents no such implementation in enough detail to claim parity. The direct comparison available here is narrower: django-stubs gives mypy advanced project-specific behavior, while hand-written application annotations concentrate typing effort in each codebase. Projects already covered by precise local types may need less global stub machinery. Projects with many ORM queries and model metadata are more likely to benefit from the plugin.
Editorial conclusion
Adopt django-stubs when a Python team uses Django with mypy and wants stricter, framework-aware checking. Do not assume its pyright, pyrefly, or ty support has the same depth, and first verify the selected django-stubs release against the project's Django, mypy, Python, and operating system dependencies.
Frequently asked questions
Is django-stubs an official Django project?
No. The README says django-stubs is independent from Django and mentions a proposal to merge the project into Django itself.
Which type checker has the best django-stubs support?
Mypy has full and complete support, including multiple advanced features supplied by the custom plugin. Pyright, pyrefly, and ty have basic support and are checked in CI.
How does django-stubs find the Django settings module?
Set `django_settings_module` in `pyproject.toml`, `mypy.ini`, or `setup.cfg`. If it is not configured, django-stubs uses `DJANGO_SETTINGS_MODULE` from the environment.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/typeddjango-django-stubs)Was this useful?