Wie Sie bereits wissen, liebe ich die optionale statische Eingabe. Die Sache ist, dass es manchmal nicht optional, aber unmöglich ist. Weil wir viele große untypisierte Projekte in Pythons Ökosystem haben.
Django und Django-Rest-Framework waren zwei davon. Waren. Denn jetzt können sie getippt werden! Lassen Sie mich Typed Django Organisation und Stubs für django
und drf
.
Dies wird ein kurzes Tutorial und eine Anleitung für die ersten Schritte sein.
Ein großes Lob
Ich möchte @mkurnikov für die Leitung des Projekts und allen Mitwirkenden, die dies ermöglicht haben, ein großes Dankeschön sagen. Ihr seid alle großartig!
TLDR
In diesem Artikel zeige ich, wie Typen mit django
und drf
. Das Ergebnis können Sie hier einsehen .
Sie können auch wemake-django-template
, um Ihre neuen Projekte mit allem zu starten, was bereits konfiguriert ist. Es sieht genauso aus wie im Beispielprojekt.
Erste Schritte
In diesem kleinen Tutorial zeige ich Ihnen einige Funktionen von django-stubs
und djangorestframework-stubs
in Aktion. Ich hoffe, das wird Sie davon überzeugen, dass es eine gute Sache ist, jemanden zu haben, der die Dinge nach Ihnen überprüft.
Sie können sich immer auf die Originaldokumentation beziehen. Dort werden auch alle Schritte behandelt.
Zu Beginn benötigen wir ein neues Projekt und eine saubere virtuelle Umgebung , damit wir unsere Abhängigkeiten installieren können:
pip install django django-stubs mypy
Dann müssen wir mypy
richtig konfigurieren. Es kann in zwei Schritte unterteilt werden. Zuerst konfigurieren wir den mypy
selbst:
# 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
Dann konfigurieren wir das django-stubs
Plugin:
# setup.cfg [mypy] # Appending to `mypy` section: plugins = mypy_django_plugin.main [mypy.plugins.django-stubs] django_settings_module = server.settings
Was machen wir hier?
- Wir fügen ein benutzerdefiniertes
mypy
Plugin hinzu, mit dessen Hilfe die Typprüfung in einigen komplizierten Django-spezifischen Situationen (wie Modellen, Abfragesätzen, Einstellungen usw.) erraten kann. - Wir fügen auch eine benutzerdefinierte Konfiguration für
django-stubs
, um auf die Einstellungen zu verweisen, die wir für Django verwenden. Es muss importiert werden.
Das Endergebnis finden Sie hier .
Wir haben jetzt alles installiert und konfiguriert. Lassen Sie uns die Dinge überprüfen!
Typechecking-Ansichten
Beginnen wir mit der Eingabe von Ansichten, da dies mit diesem Plugin am einfachsten ist.
Hier ist unsere einfache funktionsbasierte Ansicht:
Lassen Sie uns laufen und sehen, welche Typen es kennt. Beachten Sie, dass wir möglicherweise PYTHONPATH
ändern PYTHONPATH
, damit mypy
unser Projekt importieren kann:
» 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'
Versuchen wir etwas zu zerbrechen:
Nein, es gibt einen Tippfehler und mypy
wird ihn fangen:
» PYTHONPATH="$PYTHONPATH:$PWD" mypy server server/apps/main/views.py:18: error: Argument 1 to "render" has incompatible type "Dict[str, Any]"; expected "HttpRequest"
Es funktioniert! Ok, aber das ist ziemlich einfach. Lassen Sie uns unser Beispiel etwas komplizieren und ein benutzerdefiniertes Modell erstellen, um zu zeigen, wie wir Modelle und Abfragesätze eingeben können.
Typprüfmodelle und Abfragesatz
Djangos ORM ist ein Killer-Feature. Es ist sehr flexibel und dynamisch. Es bedeutet auch, dass es schwer zu tippen ist. Sehen wir uns einige Funktionen an, die bereits von django-stubs
abgedeckt werden.
Unsere Modelldefinition:
Und jedes Feld dieses Modells wird von django-stubs
abgedeckt. Mal sehen, welche Typen aufgedeckt werden:
» 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*'
Alles sieht gut aus! django-stubs
bietet ein benutzerdefiniertes mypy
Plugin zum Konvertieren von mypy
in korrekte mypy
. Deshalb werden alle Typen korrekt angezeigt.
Das zweite große Feature des django-stubs
Plugins ist, dass wir QuerySet
:
Und so kann es überprüft werden:
reveal_type(published_posts().first())
Wir können sogar Abfragesätze mit .values()
und .values_list()
-Aufrufen kommentieren. Dieses Plugin ist schlau!
Ich habe mehrere Jahre lang mit Annotationsmethoden zu kämpfen, die QuerySet
. Diese Funktion löst ein großes Problem für mich: kein Iterable[BlogPost]
oder List[User]
. Ich kann jetzt echte Typen verwenden.
Typechecking-APIs
Das Eingeben von Ansichten, Modellen, Formularen, Befehlen, URLs und Administratoren ist jedoch nicht alles, was wir haben. TypedDjango hat auch Typisierungen für djangorestframework
. Lassen Sie es uns installieren und konfigurieren:
pip install djangorestframework djangorestframework-stubs
Und wir können anfangen, Serialisierer zu erstellen:
Ansichten:
Und Router:
Es sieht nicht einmal so aus, als hätte sich etwas geändert, aber alles im Inneren ist getippt: Einstellungen, Serializer, Viewsets und Router. Sie können schrittweise Eingaben dort hinzufügen, wo Sie sie am meisten benötigen.
Versuchen wir, queryset = BlogPost.objects.all()
zu ändern.
zu queryset = [1, 2, 3]
in unseren Ansichten:
» 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]]")
Nein, es wird nicht funktionieren! Korrigieren Sie Ihren Code!
Fazit
Das Eingeben der Framework-Schnittstellen ist eine großartige Sache. In Kombination mit Tools wie returns
und mappers
können typsichere und deklarative Geschäftslogik in typisierte Framework-Schnittstellen geschrieben werden. Und um die Anzahl der Fehler in der Ebene zwischen diesen beiden zu verringern.
Mit der optionalen schrittweisen statischen Typisierung können Sie auch schnell beginnen und Typen hinzufügen, wenn Ihre API stabilisiert ist oder von Anfang an typengesteuert entwickelt wird.
django-stubs
und djangorestframework-stubs
sind jedoch neue Projekte. Es gibt immer noch viele Fehler, geplante Funktionen und fehlende Typspezifikationen. Wir freuen uns über jeden Beitrag der Community, um die Entwickler-Tools in Python wirklich großartig zu machen.
Ursprünglich in meinem Blog veröffentlicht .