Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type annotations for Django models

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?

like image 356
Djent Avatar asked Nov 25 '19 12:11

Djent


People also ask

How do you annotate in Django?

Appending the annotate() clause onto a QuerySet lets you add an attribute to each item in the QuerySet, like if you wanted to count the amount of articles in each category. However, sometimes you only want to count objects that match a certain condition, for example only counting articles that are published.

What is __ Str__ in Django model?

The __str__() method is called whenever you call str() on an object. Django uses str(obj) in a number of places. Most notably, to display an object in the Django admin site and as the value inserted into a template when it displays an object.

What are type annotations Python?

Type annotations — also known as type signatures — are used to indicate the datatypes of variables and input/outputs of functions and methods. In many languages, datatypes are explicitly stated. In these languages, if you don't declare your datatype — the code will not run.


1 Answers

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]
plugins =
    mypy_django_plugin.main

strict_optional = True

[mypy.plugins.django-stubs]
django_settings_module = demo.settings

(Don't forget to update django_settings_module according to your settings module)

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.

like image 79
bug Avatar answered Oct 03 '22 15:10

bug