Como ya sabes, me encanta la escritura estática opcional. El caso es que a veces no es opcional, sino imposible. Porque tenemos muchos proyectos grandes sin tipo en el ecosistema de Python.
Django y Django-Rest-Framework fueron dos de ellos. Estaban ¡Porque ahora se pueden escribir! Permítanme presentarles la organización Typed Django y los drf
para django
y drf
.
Este será un tutorial conciso y una guía de inicio.
Kudos
Quiero agradecer a @mkurnikov por liderar el proyecto y a todos los contribuyentes que lo hicieron posible. ¡Todos ustedes son geniales!
TLDR
En este artículo, drf
cómo funcionan los tipos con django
y drf
. Puedes ver el resultado aquí .
Y también puede usar wemake-django-template
para comenzar sus nuevos proyectos con todo lo que ya está configurado. Se verá exactamente como el proyecto de ejemplo.
Empezando
En este pequeño tutorial, le mostraré varias características de django-stubs
y djangorestframework-stubs
en acción. Espero que esto te convenza de que tener a alguien que revise las cosas después de ti es algo bueno.
Siempre puede consultar la documentación original. Todos los pasos también están cubiertos allí.
Para comenzar necesitaremos un nuevo proyecto y un entorno virtual limpio , para que podamos instalar nuestras dependencias:
pip install django django-stubs mypy
Entonces tendremos que configurar mypy
correctamente. Se puede dividir en dos pasos. Primero, configuramos el mypy
sí:
# setup.cfg [mypy] # The mypy configurations: https://mypy.readthedocs.io/en/latest/config_file.html python_version = 3.7 check_untyped_defs = True disallow_any_generics = True disallow_untyped_calls = True disallow_untyped_decorators = True ignore_errors = False ignore_missing_imports = True implicit_reexport = False strict_optional = True strict_equality = True no_implicit_optional = True warn_unused_ignores = True warn_redundant_casts = True warn_unused_configs = True warn_unreachable = True warn_no_return = True
Luego configuramos el complemento django-stubs
:
# setup.cfg [mypy] # Appending to `mypy` section: plugins = mypy_django_plugin.main [mypy.plugins.django-stubs] django_settings_module = server.settings
Que hacemos aqui
mypy
complemento mypy
personalizado para ayudar al verificador de tipos a adivinar los tipos en algunas situaciones complicadas específicas de Django (como modelos, conjunto de consultas, configuraciones, etc.)- También agregamos una configuración personalizada para
django-stubs
para señalarla a la configuración, la usamos para Django. Tendrá que importarlo.
El resultado final se puede encontrar aquí .
Ahora tenemos todo instalado y configurado. ¡Escribamos cosas de cheques!
Vistas de comprobación de tipo
Comencemos escribiendo las vistas, ya que es lo más fácil de hacer con este complemento.
Aquí está nuestra vista simple basada en funciones:
Corramos y veamos de qué tipos es consciente. Tenga en cuenta que es posible que necesitemos modificar PYTHONPATH
, por lo que mypy
podría importar nuestro proyecto:
» PYTHONPATH="$PYTHONPATH:$PWD" mypy server server/apps/main/views.py:14: note: Revealed type is 'def () -> builtins.bool' server/apps/main/views.py:15: note: Revealed type is 'django.contrib.auth.models.User'
Intentemos romper algo:
No, hay un error tipográfico y mypy
lo detectará:
» PYTHONPATH="$PYTHONPATH:$PWD" mypy server server/apps/main/views.py:18: error: Argument 1 to "render" has incompatible type "Dict[str, Any]"; expected "HttpRequest"
Funciona! Ok, pero eso es bastante sencillo. Vamos a complicar un poco nuestro ejemplo y crear un modelo personalizado para mostrar cómo podemos escribir modelos y conjuntos de consultas.
Modelos de comprobación de tipo y conjunto de consultas
El ORM de Django es una característica asesina. Es muy flexible y dinámico. También significa que es difícil de escribir. Veamos algunas características que ya están cubiertas por django-stubs
.
Nuestra definición del modelo:
Y cada campo de este modelo está cubierto por django-stubs
. Veamos qué tipos se revelan:
» PYTHONPATH="$PYTHONPATH:$PWD" mypy server server/apps/main/models.py:21: note: Revealed type is 'builtins.int*' server/apps/main/models.py:22: note: Revealed type is 'django.contrib.auth.models.User*' server/apps/main/models.py:23: note: Revealed type is 'builtins.str*' server/apps/main/models.py:24: note: Revealed type is 'builtins.bool*' server/apps/main/models.py:25: note: Revealed type is 'datetime.datetime*'
¡Todo se ve bien! django-stubs
proporciona un complemento mypy
personalizado para convertir campos de modelo en tipos de instancia correctos. Es por eso que todos los tipos se revelan correctamente.
La segunda gran característica del complemento django-stubs
es que podemos escribir QuerySet
:
Y así es como se puede verificar:
reveal_type(published_posts().first())
Incluso podemos anotar conjuntos de consultas con .values()
y .values_list()
. ¡Este complemento es inteligente!
He luchado con los métodos de anotación que devuelven QuerySet
s durante varios años. Esta característica me resuelve un gran problema: no más Iterable[BlogPost]
o List[User]
. Ahora puedo usar tipos reales.
API de comprobación de tipo
Pero, escribir vistas, modelos, formularios, comandos, URL y admin no es todo lo que tenemos. TypedDjango también tiene tipings para djangorestframework
. Vamos a instalarlo y configurarlo:
pip install djangorestframework djangorestframework-stubs
Y podemos comenzar a crear serializadores:
Vistas:
Y enrutadores:
Ni siquiera parece que algo haya cambiado, pero todo lo que está dentro está escrito: configuraciones, serializadores, vistas y enrutadores. Le permitirá agregar incrementalmente las tipificaciones donde más las necesita.
Intentemos cambiar queryset = BlogPost.objects.all()
a queryset = [1, 2, 3]
en nuestras vistas:
» PYTHONPATH="$PYTHONPATH:$PWD" mypy server server/apps/main/views.py:25: error: Incompatible types in assignment (expression has type "List[int]", base class "GenericAPIView" defined the type as "Optional[QuerySet[Any]]")
¡No, no funcionará! ¡Arregla tu código!
Conclusión
Escribir las interfaces de framework es algo increíble. Cuando se combina con herramientas como returns
y mappers
, permitirá escribir una lógica comercial declarativa y segura de tipos envuelta en interfaces de marco escritas. Y para disminuir el número de errores en la capa entre estos dos.
La escritura estática gradual opcional también le permite comenzar rápidamente y agregar tipos solo cuando su API está estabilizada o ir con un desarrollo basado en tipos desde el principio.
Sin embargo, django-stubs
y djangorestframework-stubs
son proyectos nuevos. Todavía hay muchos errores, características planificadas, especificaciones de tipo faltantes. Damos la bienvenida a cada contribución de la comunidad para hacer que las herramientas de desarrollador en Python sean realmente increíbles.
Publicado originalmente en mi blog .