Reputation: 3467
I'm working on a Django project. Since this is a new project, I want to have it fully annotated with python 3.6+ type annotations. I'm trying to annotate models, but I struggle to find a good method for that.
Let's take the IntegerField
as an example. I see two choices for annotating it:
# number 1
int_field: int = models.IntegerField()
# number 2
int_field: models.IntegerField = models.IntegerField()
Number 1 fails in mypy:
Incompatible types in assignment (expression has type "IntegerField[<nothing>, <nothing>]", variable has type "int")
Number 2 is OK for mypy, but IDE's as PyCharm are not able to resolve it and are often complaining about wrong types used.
Are there any best practices to correctly annotate the models, which will satisfy mypy and IDE's?
Upvotes: 46
Views: 24214
Reputation: 2827
If you're using pyproject.toml
, here's how you can configure mypy
to work with django:
django-stubs
:# If using 'poetry'
$ poetry add 'django-stubs[compatible-mypy]' --group dev`
# If using 'pip'
$ pip install 'django-stubs[compatible-mypy]'
pyproject.toml
to point to your source dir and your django settings module:[tool.mypy]
# ...
mypy_path = "."
plugins = ["mypy_django_plugin.main"]
[tool.django-stubs]
django_settings_module = "YOUR_PROJECT_PACKAGE.settings"
NOTE: Different django
, django-stubs
and mypy
versions have different compatibilities. Check the table from here: https://pypi.org/project/django-stubs/
Upvotes: 1
Reputation: 4130
Django models (and other components) are hard to annotate because there is a lot of magic behind them, good news is that a group of cool developers have already done the hard work for us.
django-stubs provides a set of stubs and mypy plugins which provide static types and type inference for Django.
For instance, having the following model:
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Post(models.Model):
title = models.CharField(max_length=255)
pubdate = models.DateTimeField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
mypy would complain saying:
demo$ mypy .
demo/models.py:9: error: Need type annotation for 'title'
demo/models.py:10: error: Need type annotation for 'pubdate'
demo/models.py:11: error: Need type annotation for 'author'
Found 3 errors in 1 file (checked 5 source files)
To fix it, it's enough to install the package
pip install django-stubs
and create a setup.cfg
file with the following:
[mypy]
mypy_path = ./demo
plugins =
mypy_django_plugin.main
strict_optional = True
[mypy.plugins.django-stubs]
django_settings_module = demo.settings
(Don't forget to update mypy_path
and django_settings_module
to point to the directory of your settings module, according to how yours is named)
Once this is done, mypy will be able to infer and check annotations for Django models (and other components).
demo$ mypy .
Success: no issues found in 5 source files
Here is an example of the usage in a small view:
from django.db.models.query import QuerySet
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
from demo.models import Post
def _get_posts() -> 'QuerySet[Post]':
return Post.objects.all()
def posts(request: HttpRequest, template: str='posts.html') -> HttpResponse:
return render(request, template, {'posts': _get_posts()})
Once again, mypy is happy with the provided annotations:
demo$ mypy .
Success: no issues found in 7 source files
On the same note, a package for Django Rest Framework is also available: djangorestframework-stubs.
Upvotes: 66