Comme vous le savez déjà, j'aime la frappe statique optionnelle. Le fait est que parfois ce n'est pas facultatif, mais impossible. Parce que nous avons beaucoup de grands projets non typés dans l'écosystème de Python.
Django et Django-Rest-Framework étaient deux d'entre eux. Étaient. Parce que maintenant ils peuvent être tapés! Permettez-moi de vous présenter l'organisation django
Django et les django
pour django
et drf
.
Cela va être un tutoriel concis et un guide de démarrage.
Bravo
Je tiens à dire un grand merci à @mkurnikov pour avoir dirigé le projet et à tous les contributeurs qui ont rendu cela possible. Vous êtes tous géniaux!
TLDR
Dans cet article, je montre comment les types fonctionnent avec django
et drf
. Vous pouvez voir le résultat ici .
Et vous pouvez également utiliser wemake-django-template
pour démarrer vos nouveaux projets avec tout ce qui est déjà configuré. Il ressemblera exactement à l'exemple de projet.
Pour commencer
Dans ce petit tutoriel, je vais vous montrer plusieurs fonctionnalités de django-stubs
djangorestframework-stubs
et djangorestframework-stubs
en action. J'espère que cela vous convaincra qu'avoir quelqu'un pour vérifier les choses après vous est une bonne chose.
Vous pouvez toujours vous référer à la documentation d'origine. Toutes les étapes y sont également abordées.
Pour commencer, nous aurons besoin d' un nouveau projet et d' un environnement virtuel propre , afin que nous puissions installer nos dépendances:
pip install django django-stubs mypy
Ensuite, nous devrons configurer correctement mypy
. Il peut être divisé en deux étapes. Tout d'abord, nous configurons le mypy
lui-même:
# 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
Ensuite, nous configurons le plugin django-stubs
:
# setup.cfg [mypy] # Appending to `mypy` section: plugins = mypy_django_plugin.main [mypy.plugins.django-stubs] django_settings_module = server.settings
Que faisons-nous ici?
- Nous ajoutons un plugin
mypy
personnalisé pour aider le vérificateur de types à deviner les types dans certaines situations complexes spécifiques à Django (comme les modèles, le jeu de requêtes, les paramètres, etc.) - Nous ajoutons également une configuration personnalisée pour
django-stubs
pour la pointer vers les paramètres que nous utilisons pour Django. Il devra l'importer.
Le résultat final peut être trouvé ici .
Nous avons maintenant tout installé et configuré. Tapons des choses de contrôle!
Vérification des vues
Commençons par taper les vues car c'est la chose la plus simple à faire avec ce plugin.
Voici notre simple vue basée sur les fonctions:
Courons et voyons de quels types il est au courant. Notez que nous devrons peut-être modifier PYTHONPATH
, afin que mypy
puisse importer notre projet:
» 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'
Essayons de casser quelque chose:
Non, il y a une faute de frappe et mypy
l'attrapera:
» PYTHONPATH="$PYTHONPATH:$PWD" mypy server server/apps/main/views.py:18: error: Argument 1 to "render" has incompatible type "Dict[str, Any]"; expected "HttpRequest"
Ça marche! D'accord, mais c'est assez simple. Compliquons un peu notre exemple et créons un modèle personnalisé pour montrer comment taper des modèles et des ensembles de requêtes.
Vérification des modèles et jeu de requêtes
L'ORM de Django est une fonctionnalité qui tue. Il est très flexible et dynamique. Cela signifie également qu'il est difficile à taper. Voyons quelques fonctionnalités déjà couvertes par django-stubs
.
Notre définition de modèle:
Et chaque domaine de ce modèle est couvert par des django-stubs
. Voyons quels types sont révélés:
» 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*'
Tout a l'air bien! django-stubs
fournit un plugin mypy
personnalisé pour convertir les champs du modèle en types d'instance corrects. C'est pourquoi tous les types sont correctement révélés.
La deuxième grande fonctionnalité du plugin django-stubs
est que nous pouvons taper QuerySet
:
Et voici comment cela peut être vérifié:
reveal_type(published_posts().first())
Nous pouvons même annoter des ensembles de requêtes avec des .values()
et .values_list()
. Ce plugin est intelligent!
J'ai du mal avec les méthodes d'annotation renvoyant des QuerySet
depuis plusieurs années. Cette fonctionnalité résout un gros problème pour moi: plus d' Iterable[BlogPost]
ou de List[User]
. Je peux maintenant utiliser de vrais types.
Vérification des API
Mais, taper des vues, des modèles, des formulaires, des commandes, des URL et des administrateurs n'est pas tout ce que nous avons. TypedDjango a également des typages pour djangorestframework
. Installons-le et configurons-le:
pip install djangorestframework djangorestframework-stubs
Et nous pouvons commencer à créer des sérialiseurs:
Vues:
Et les routeurs:
Il ne semble même pas que quelque chose ait changé, mais tout à l'intérieur est tapé: paramètres, sérialiseurs, ensembles de vues et routeurs. Il vous permettra d'ajouter progressivement des saisies là où vous en avez le plus besoin.
Essayons de changer queryset = BlogPost.objects.all()
to queryset = [1, 2, 3]
dans nos vues:
» 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]]")
Non, ça ne marchera pas! Réparez votre code!
Conclusion
Taper les interfaces du framework est une chose géniale à avoir. Lorsqu'il est combiné avec des outils tels que les returns
et les mappers
il permettra d'écrire une logique métier déclarative et sécurisée enveloppée dans des interfaces de framework typées. Et pour diminuer le nombre d'erreurs dans la couche entre ces deux.
La saisie statique progressive facultative vous permet également de démarrer rapidement et d'ajouter des types uniquement lorsque votre API est stabilisée ou de suivre un développement axé sur les types dès le début.
Cependant, django-stubs
et djangorestframework-stubs
sont de nouveaux projets. Il y a encore beaucoup de bugs, de fonctionnalités planifiées, de spécifications de type manquantes. Nous accueillons chaque contribution de la communauté pour rendre les outils de développement en Python vraiment géniaux.
Publié à l'origine sur mon blog .