Anotações de tipo para modelos do Django

8

Estou trabalhando em um projeto Django. Como este é um projeto novo, desejo que ele seja totalmente anotado com anotações do tipo python 3.6+. Estou tentando anotar modelos, mas luto para encontrar um bom método para isso.

Vamos dar o IntegerFieldexemplo. Vejo duas opções para anotá-lo:

# number 1
int_field: int = models.IntegerField()

# number 2
int_field: models.IntegerField = models.IntegerField()

O número 1 falha no mypy:

Incompatible types in assignment (expression has type "IntegerField[<nothing>, <nothing>]", variable has type "int")

O número 2 é bom para mypy, mas os IDE como PyCharm não conseguem resolvê-lo e geralmente reclamam dos tipos errados usados.

Existem práticas recomendadas para anotar corretamente os modelos, o que satisfará mypy e IDE?

Djent
fonte
3
Como você parece investir muito tempo em ferramentas satisfatórias, pode se surpreender ao saber que o que você faz não é para o que a anotação de tipo não foi projetada: digitação estática.
Klaus D.
Você não está procurando algo como mypy-django ?
Pedram Parsian
Parece que o mypy-django não oferece anotações de tipo para modelos: github.com/machinalis/mypy-django-example/blob/master/polls/…
Clément Denoix
@KlausD. - Não sei se entendi o que você está dizendo, pode elaborar?
Djent
portanto, mesmo se você resolver isso para IntegerField (), o que você planeja para um campo de 50 caracteres? É meio bom saber que isso é meio que uma string, mas não é realmente. Vou fazer uma pergunta ingênua, mas eles já têm tipos. Você está tentando convertê-los em tipos básicos, parece-me .. que problema você quer resolver exatamente? É um problema bastante difícil ... veja até onde Rust chegou com isso.
Tim Richardson

Respostas:

8

Os modelos do Django (e outros componentes) são difíceis de anotar porque há muita mágica por trás deles, uma boa notícia é que um grupo de desenvolvedores legais já fez o trabalho duro por nós.

O django-stubs fornece um conjunto de stubs e plugins mypy que fornecem tipos estáticos e inferência de tipos para o Django.

Por exemplo, com o seguinte modelo:

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 reclamaria dizendo:

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)

Para corrigi-lo, basta instalar o pacote

pip install django-stubs

e crie um setup.cfgarquivo com o seguinte:

[mypy]
plugins =
    mypy_django_plugin.main

strict_optional = True

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

(Não esqueça de atualizar de django_settings_moduleacordo com o seu módulo de configurações)

Feito isso, o mypy poderá inferir e verificar anotações para modelos do Django (e outros componentes).

demo$ mypy .
Success: no issues found in 5 source files

Aqui está um exemplo do uso em uma exibição pequena:

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()})

Mais uma vez, mypy está satisfeito com as anotações fornecidas:

demo$ mypy .
Success: no issues found in 7 source files

Na mesma nota, também está disponível um pacote para o Django Rest Framework: djangorestframework-stubs .

erro
fonte
Na verdade, eu encontrei isso, mas usei errado. Obrigado pela resposta detalhada que me ajudou a seguir em frente.
Djent