# djangorestframework-stubs gives mypy the types Django REST Framework leaves out

> djangorestframework-stubs is a PEP 484 stub package for Django REST Framework, shipping a mypy plugin that reads the runtime serializer configuration so that a ModelSerializer subclass is typed as its own model rather than as Any.

**typeddjango/djangorestframework-stubs** — PEP-484 stubs for django-rest-framework

- Repository: https://github.com/typeddjango/djangorestframework-stubs
- Stars: 541 · Forks: 146
- Language: Python
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/typeddjango-djangorestframework-stubs

## Stubs fill the gap left by an untyped framework

Django REST Framework is popular and widely used, and it does not ship inline type annotations, so a type checker sees most of its API as untyped. djangorestframework-stubs is the community answer: a package of PEP 484 stub files for the framework, so that mypy can check calls into it.

The package is deliberately narrow. It installs as djangorestframework-stubs, it requires Python 3.11 and up, and its runtime dependencies are small and specific: django-stubs at 6.1.0 or newer, typing-extensions, and the types for PyYAML. The optional extras exist for the same reason, with coreapi, markdown and requests type stubs available as separate extras for projects that need them.

The style of the project is worth noting before you look at the code. The classifiers include Typing :: Typed, so the package itself is annotated, and the supported Python versions run from 3.11 through 3.14. The authorship is a small group of named maintainers with a funding link in the project metadata, which is the standard shape for a package that has outlived its original author.

## Installation is one pip command and one ini entry

The install pulls the stubs along with a mypy version that is known to work with them, and the extra does that pinning for you:

```bash
pip install djangorestframework-stubs[compatible-mypy]
```

The extra names compatible-mypy and depends on mypy at 1.13 or newer and below 2.5, plus the matching django-stubs extra. Installing the stubs without it is possible, but then you are responsible for the mypy version yourself, and mismatches between a checker version and a stub package are the usual cause of a plugin failing to load.

The second step is the one people miss. The stubs ship a mypy plugin, and mypy has to be told about it in the configuration file:

```ini
[mypy]
plugins =
    mypy_drf_plugin.main
```

That goes in your mypy.ini. Without it the stub files are still used, but the plugin's extra inference does not happen, which is where most of the value of this package lives.

## The plugin is what turns a serializer into a typed view

The plugin exists because the interesting typing information in Django REST Framework is not in the function signatures. It is in the class configuration: which model a serializer is bound to, which fields it declares, and how those relate to the request and response data. A stub file cannot express that, so a checker has to read the runtime configuration, which is what the plugin does.

The payoff is described in the repository's own FAQ, and it is a single symptom. When a serializer is used, the model instance is often inferred as Any instead of the model class you meant. The reason is that ModelSerializer is generic in the model type, and the type argument has to be supplied or the checker has nothing to bind to.

So the fix is to add a type argument when you subclass the serializer:

```python
class MyModelSerializer(serializers.ModelSerializer[MyModel]):
    class Meta:
        model = MyModel
        fields = ("id", "example")
```

With that annotation, the methods that pass the model around know the real type instead of Any, and the instance attribute on the serializer is typed as the model, a sequence of the model, or None. That single change is what turns a checker run from noisy to useful on a DRF codebase.

## The repository is stubs, a plugin, and a test suite

The top-level entries describe the shape of the project. There is the rest_framework-stubs directory holding the stub files themselves, the mypy_drf_plugin directory holding the plugin, and a tests directory. Around them sit the usual repository files: a mypy.ini for the project itself, a pyproject.toml, an AGENTS.md, a CONTRIBUTING.md, a pre-commit configuration and a .github directory with the test workflow that the README badges point at.

The testing approach is the interesting part. The development dependencies include pytest-mypy-plugins, which is a framework for writing tests as data files that assert on mypy's output. That is how a stub package proves its behaviour, since a stub that does not produce the expected error or the expected inference is broken even when it imports cleanly.

The development group also pins the real framework, djangorestframework 3.18.3, and Django itself, with the version chosen by the interpreter, 5.2 below Python 3.12 and 6.1 at 3.12 and above. The django-stubs packages are pulled from git rather than from the index, and there is a separate primer group for mypy-primer, the tool that measures how a stub package changes the checker's results on a large body of real code.

## Version numbers track the framework, and the plugin tracks mypy

The release history is short and regular. The recent versions are 3.18.1 on 2026-08-25, 3.18.0 on 2026-08-12 and 3.17.1 on 2026-07-28, and the last push to the repository was on 2026-10-19. The repository is not archived.

The numbering is the interesting detail. The package version is 3.18.1 while the framework version it is developed against is 3.18.3, so the two numbers track each other without being identical, which is the usual arrangement for a stubs package that has to be released independently of the library it describes.

The other constraint is mypy itself. The compatible-mypy extra caps mypy below 2.5, which tells you that the plugin API is not guaranteed to survive a major checker release, and that a mypy upgrade can require a stubs upgrade with it. The dependency groups make that coupling explicit by pinning the checker, the framework, Django and the django-stubs packages together in one place, so a developer working on the stubs runs the combination the package claims to support.

## Where the stubs stop and the framework keeps going

A stub package can only describe what its authors wrote down, and the boundary shows up in a few predictable places.

The most visible one is the plugin's coverage. The FAQ shows a single symptom, the model instance inferred as Any, and the answer is a type argument. That means the inference is opt-in per serializer. A codebase with hundreds of serializers and no type arguments gets the benefit of the stub files, which is the plain typing of framework calls, and not the benefit of the plugin, which is the model-aware inference. Expect to add annotations to your own code as part of the migration, and treat that as work rather than configuration.

The second boundary is the version floor. Python 3.11 and up is required, and the Django version is tied to the django-stubs requirement of 6.1.0 or newer, so a project on an older Django or an older Python is out of scope for the current release.

The third is that this is a mypy story. The plugin is a mypy plugin, the extras are about mypy versions, and the primer group is a mypy benchmarking tool. A team that type checks with pyright will get the stub files, which is most of the benefit, but not the model-aware inference, because that part is a mypy extension.

## Licence, support channels and how to contribute

The package is licensed under MIT, with the licence text in LICENSE.txt and the licence declared in the project metadata, and the package is marked as typed in its classifiers.

Support is community based, and the README names the channels rather than promising support. There is a Gitter room for the project, and a StackOverflow tag for django-stubs questions. The README also points out that a general typing question is better taken to mypy's own Gitter, which is a sensible division: the framework-specific behaviour belongs here, the checker behaviour belongs there.

Contributions are invited in three forms, and the first one is the substantive one. Code contributions, such as improving stubs, adding plugin capabilities or writing tests, follow the contribution guide in CONTRIBUTING.md. The other two are helping with code review and discussions in issues, and reporting bugs.

The authors are credited in the metadata, and the project has a funding link, which is the arrangement for the packages in the typeddjango family.

## Conclusion

djangorestframework-stubs fits a Python team that runs mypy on a Django REST Framework codebase and wants serializer methods and instance attributes to be typed rather than Any, and who are willing to add a plugin to their mypy configuration. It is the wrong tool if you do not run mypy, or if you are on a Python older than 3.11, which is the stated floor, or if your type checking is done by a different checker that will not read the plugin. Before you adopt it, check three things: that your Django version lines up with the django-stubs version the package requires, which is 6.1.0 or newer, that you add the plugin to mypy.ini and not only to a command line, and that you parameterise your ModelSerializer subclasses, since the generic argument is what turns the instance attribute from Any into the model type. The licence is MIT, the current version is 3.18.1, and the last push was on 2026-10-19.

## FAQ

### How do I install djangorestframework-stubs?

Use `pip install djangorestframework-stubs[compatible-mypy]`, which also pins a mypy version known to work with the stubs. Python 3.11 or newer is required.

### How do I enable the mypy plugin for djangorestframework-stubs?

Add the plugin to your mypy.ini under a [mypy] section with `plugins =` and the entry `mypy_drf_plugin.main`. Without that entry the stub files still apply, but the model-aware inference does not.

### Why is my model instance inferred as Any in my serializer?

Because ModelSerializer is generic in the model type. Add the type argument when subclassing, for example `class MyModelSerializer(serializers.ModelSerializer[MyModel]):`, and the instance attribute becomes the model, a sequence of the model, or None.

### What are the runtime dependencies of the stub package?

django-stubs at 6.1.0 or newer, typing-extensions at 4.0 or newer, and types-PyYAML. Optional extras add coreapi, markdown and requests type stubs, and the compatible-mypy extra adds a matching mypy and django-stubs.

### How are the stubs themselves tested?

The development dependencies include pytest-mypy-plugins, which runs tests written as data files asserting on mypy's output, alongside pytest, and the stubs are tested against pinned versions of Django REST Framework, Django and django-stubs.

## Sources

- [Issues](https://github.com/typeddjango/djangorestframework-stubs/issues)
- [License: MIT](https://github.com/typeddjango/djangorestframework-stubs/blob/master/LICENSE)
- [README](https://github.com/typeddjango/djangorestframework-stubs/blob/master/README.md)
- [Releases](https://github.com/typeddjango/djangorestframework-stubs/releases)
- [typeddjango/djangorestframework-stubs on GitHub](https://github.com/typeddjango/djangorestframework-stubs)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/typeddjango-djangorestframework-stubs
