Escribir anotaciones para modelos Django

8

Estoy trabajando en un proyecto de Django. Dado que este es un proyecto nuevo, quiero que esté completamente anotado con anotaciones de tipo python 3.6+. Estoy tratando de anotar modelos, pero me cuesta encontrar un buen método para eso.

Tomemos el IntegerFieldcomo ejemplo. Veo dos opciones para anotarlo:

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

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

El número 1 falla en mypy:

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

El número 2 está bien para mypy, pero los IDE como PyCharm no pueden resolverlo y a menudo se quejan de los tipos incorrectos utilizados.

¿Existen mejores prácticas para anotar correctamente los modelos, que satisfagan mypy e IDE?

Djent
fuente
3
Como parece que invierte mucho tiempo en herramientas satisfactorias, puede que se sorprenda al saber que lo que hace no es para lo que la anotación de tipo no está diseñada: la escritura estática.
Klaus D.
¿No estás buscando algo como mypy-django ?
Pedram Parsian
Parece que mypy-django no ofrece anotaciones de tipo para modelos: github.com/machinalis/mypy-django-example/blob/master/polls/…
Clément Denoix
@KlausD. - No estoy seguro si entiendo lo que estás diciendo, ¿puedes dar más detalles?
Djent
así que, incluso si resuelve esto para IntegerField (), ¿qué planea para un campo de 50 caracteres? Es bueno saber que es una especie de cadena, pero en realidad no lo es. Voy a hacer una pregunta ingenua, pero ya tienen tipos. Estás intentando convertirlos en tipos básicos, me parece ... ¿qué problema quieres resolver exactamente? Es un problema bastante difícil ... mira hasta dónde ha llegado Rust.
Tim Richardson el

Respuestas:

8

Los modelos de Django (y otros componentes) son difíciles de anotar porque hay mucha magia detrás de ellos, una buena noticia es que un grupo de desarrolladores geniales ya han hecho el trabajo duro por nosotros.

django-stubs proporciona un conjunto de stubs y complementos mypy que proporcionan tipos estáticos e inferencia de tipos para Django.

Por ejemplo, tener el siguiente 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 se quejaría diciendo:

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 solucionarlo, es suficiente instalar el paquete

pip install django-stubs

y crea un setup.cfgarchivo con lo siguiente:

[mypy]
plugins =
    mypy_django_plugin.main

strict_optional = True

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

(No olvide actualizar django_settings_modulesegún su módulo de configuración)

Una vez hecho esto, mypy podrá inferir y verificar anotaciones para los modelos de Django (y otros componentes).

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

Aquí hay un ejemplo del uso en una vista pequeña:

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

Una vez más, mypy está contento con las anotaciones proporcionadas:

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

En la misma nota, también está disponible un paquete para Django Rest Framework: djangorestframework-stubs .

insecto
fuente
De hecho, he encontrado esto, pero lo he usado mal. Gracias por la respuesta detallada que me ayudó a seguir adelante.
Djent