Optimieren Sie Ihre Django-Einstellungen mit Type Hints: A Pydantic Tutorial

Veröffentlicht: 2022-07-22

Django-Projekte haben mich früher frustriert, weil mir eine robuste und skalierbare Möglichkeit zum Hinzufügen neuer Umgebungen fehlte. Indem ich pydantische und Python-artige Hinweise zusammenbrachte, baute ich die leistungsstarke Grundlage auf, die ich brauchte.

Wie in PEP 484 beschrieben, unterstützen Typhinweise die statische Analyse, aber dieselben Anmerkungen sind auch zur Laufzeit verfügbar. Pakete von Drittanbietern wie pydantic bieten Laufzeittypprüfungen an, die diese zusätzlichen Metadaten verwenden. Pydantic verwendet Hinweise vom Typ Python, um die Verwaltung von Einstellungsmetadaten und die Validierung von Laufzeitdaten zu unterstützen.

Dieses pydantic-Tutorial zeigt die weitreichenden positiven Auswirkungen der Verwendung der pydantic-Einstellungsverwaltung mit Django.

Unsere Konfiguration entspricht den Best Practices, die auf der Website der Twelve-Factor App beschrieben sind:

  1. Definieren Sie nicht konstante und geheime Konfigurationen als Umgebungsvariablen.
  2. Definieren Sie in Entwicklungsumgebungen Umgebungsvariablen in einer .env -Datei und fügen Sie die .env -Datei zu .gitignore .
  3. Verwenden Sie die Mechanismen des Cloud-Anbieters, um (geheime) Umgebungsvariablen für die QA-, Staging- und Produktionsumgebungen zu definieren.
  4. Verwenden Sie eine einzelne settings.py -Datei, die sich selbst anhand der Umgebungsvariablen konfiguriert.
  5. Verwenden Sie pydantic, um Umgebungsvariablen zu lesen, zu prüfen, zu validieren und in Python-Variablen umzuwandeln, die die Django-Konfigurationen definieren.

Alternativ erstellen einige Entwickler mehrere Einstellungsdateien wie settings_dev.py und settings_prod.py . Leider lässt sich dieser Ansatz nicht gut skalieren. Dies führt zu Codeduplizierung, Verwirrung, schwer zu findenden Fehlern und höherem Wartungsaufwand.

Mit den oben genannten Best Practices ist das Hinzufügen einer beliebigen Anzahl von Umgebungen einfach, klar definiert und fehlerfrei. Obwohl wir eine kompliziertere Umgebungskonfiguration untersuchen könnten, konzentrieren wir uns der Klarheit halber auf zwei: Entwicklung und Produktion.

Wie sieht das in der Praxis aus?

Pydantic-Einstellungsverwaltung und Umgebungsvariablen

Wir konzentrieren uns nun auf ein Beispiel sowohl in der Entwicklung als auch in der Produktion. Wir zeigen, wie jede Umgebung ihre Einstellungen anders konfiguriert und wie pydantic jede unterstützt.

Unsere Beispielanwendung erfordert eine von Django unterstützte Datenbank, daher müssen wir die Datenbankverbindungszeichenfolge speichern. Wir verschieben die Konfigurationsinformationen für die Datenbankverbindung mithilfe des Python-Pakets dj-database-url in eine Umgebungsvariable, DATABASE_URL . Bitte beachten Sie, dass diese Variable vom Typ str ist und wie folgt formatiert ist:

 postgres://{user}:{password}@{hostname}:{port}/{database-name} mysql://{user}:{password}@{hostname}:{port}/{database-name} oracle://{user}:{password}@{hostname}:{port}/{database-name} sqlite:///PATH

In unserer Entwicklungsumgebung können wir aus Gründen der Benutzerfreundlichkeit eine in Docker enthaltene PostgreSQL-Instanz verwenden, während wir in unserer Produktionsumgebung auf einen bereitgestellten Datenbankdienst verweisen.

Eine weitere Variable, die wir definieren möchten, ist ein boolescher Wert, DEBUG . Das DEBUG-Flag in Django darf in einer Produktionsbereitstellung niemals aktiviert werden. Es soll zusätzliches Feedback während der Entwicklung erhalten. Im Debug-Modus zeigt Django beispielsweise detaillierte Fehlerseiten an, wenn eine Ausnahme auftritt.

Unterschiedliche Werte für Entwicklung und Produktion könnten wie folgt definiert werden:

Variablennamen Entwicklung Produktion
DATABASE_URL postgres://postgres:mypw@localhost:5432/mydb postgres://foo1:foo2@foo3:5432/foo4
DEBUG True False

Wir verwenden das pydantic-Einstellungsverwaltungsmodul, um diese verschiedenen Sätze von Umgebungsvariablenwerten je nach Umgebung zu verwalten.

Vorbereitende Schritte

Um dies in die Praxis umzusetzen, beginnen wir mit der Konfiguration unserer Entwicklungsumgebung, indem wir unsere einzelne .env -Datei mit diesem Inhalt erstellen:

 DATABASE_URL=postgres://postgres:mypw@localhost:5432/mydb DEBUG=True

Als Nächstes fügen wir die .env -Datei der .gitignore-Datei des Projekts .gitignore . Die .gitignore -Datei vermeidet das Speichern potenziell sensibler Informationen in der Quellcodeverwaltung.

Während dieser Ansatz in unserer Entwicklungsumgebung gut funktioniert, verwendet unsere Produktionsumgebungsspezifikation einen anderen Mechanismus. Unsere Best Practices schreiben vor, dass Produktionsumgebungsvariablen Umgebungsgeheimnisse verwenden. Bei Heroku werden diese Geheimnisse beispielsweise als Konfigurationsvariablen bezeichnet und über das Heroku-Dashboard konfiguriert. Sie werden der bereitgestellten Anwendung als Umgebungsvariablen zur Verfügung gestellt:

Ein Screenshot der Weboberfläche von Config Vars. Die linke Seitenleiste enthält eine Beschreibung: „Konfigurationsvariablen ändern das Verhalten Ihrer App. Einige Add-Ons können nicht nur eigene erstellen, sondern auch eigene.“ Der Hauptbereich besteht aus zwei Zeilen mit jeweils zwei Textfeldern, einem Stiftsymbol und einem X-Symbol. Die Textfelder haben dieselben Daten wie die Spalten „Variablenname“ und „Produktion“ in der vorherigen Tabelle. In der oberen rechten Ecke befindet sich eine Schaltfläche mit der Aufschrift „Konfigurationsvariablen ausblenden“.

Danach müssen wir die Konfiguration der Anwendung anpassen, um diese Werte automatisch aus beiden Umgebungen zu lesen.

Konfigurieren der settings.py von Django

Beginnen wir mit einem neuen Django-Projekt, um die wesentliche Struktur für unser Beispiel bereitzustellen. Wir bauen ein neues Django-Projekt mit dem folgenden Terminalbefehl auf:

 $ django-admin startproject mysite

Wir haben jetzt eine grundlegende Projektstruktur für das mysite Projekt. Die Dateistruktur des Projekts ist wie folgt:

 mysite/ manage.py mysite/ __init__.py settings.py urls.py asgi.py wsgi.py

Die Datei settings.py enthält Boilerplate-Code, mit dem wir die Anwendungskonfiguration verwalten können. Es hat viele vordefinierte Standardeinstellungen, die wir an die jeweilige Umgebung anpassen müssen.

Um diese Anwendungseinstellungen mit Umgebungsvariablen und Pydantic zu verwalten, fügen Sie diesen Code am Anfang der Datei settings.py hinzu:

 import os from pathlib import Path from pydantic import ( BaseSettings, PostgresDsn, EmailStr, HttpUrl, ) import dj_database_url # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent class SettingsFromEnvironment(BaseSettings): """Defines environment variables with their types and optional defaults""" DATABASE_URL: PostgresDsn DEBUG: bool = False class Config: """Defines configuration for pydantic environment loading""" env_file = str(BASE_DIR / ".env") case_sensitive = True config = SettingsFromEnvironment() os.environ["DATABASE_URL"] = config.DATABASE_URL DATABASES = { "default": dj_database_url.config(conn_max_age=600, ssl_require=True) } DEBUG = config.DEBUG

Dieser Code macht Folgendes:

  • Definiert eine Klasse SettingsFromEnvironment , die von der Klasse BaseSettings von pydantic BaseSettings .
  • Definiert DATABASE_URL und DEBUG , legt deren Typ und optionale Vorgabe mit Python-Typhinweisen fest.
  • Definiert eine Klassenkonfiguration, die Config anweist, nach den Variablen in einer .env -Datei zu suchen, wenn sie nicht in den Umgebungsvariablen des Systems vorhanden sind.
  • Instantiiert die Config -Klasse in das Objekt config ; die gewünschten Variablen werden als config.DATABASE_URL und config.DEBUG .
  • Definiert die regulären Django-Variablen DATABASES und DEBUG aus diesen config .

Derselbe Code läuft in allen Umgebungen und pydantic kümmert sich um Folgendes:

  • Es sucht nach den Umgebungsvariablen DATABASE_URL und DEBUG .
    • Wenn sie als Umgebungsvariablen definiert sind, wie in der Produktion, werden diese verwendet.
    • Andernfalls werden diese Werte aus der .env -Datei abgerufen.
    • Wenn es keinen Wert findet, wird es wie folgt vorgehen:
      • Für DATABASE_URL wird ein Fehler ausgegeben.
      • Für DEBUG weist es einen Standardwert von False zu.
  • Wenn es eine Umgebungsvariable findet, überprüft es die Feldtypen und gibt einen Fehler aus, wenn einer von ihnen falsch ist:
    • Für DATABASE_URL überprüft es, ob sein Feldtyp eine URL im PostgresDsn -Stil ist.
    • Für DEBUG überprüft es, ob sein Feldtyp ein gültiger, nicht strikter pydantischer boolescher Wert ist.

Bitte beachten Sie die explizite Einstellung der Umgebungsvariable des Betriebssystems aus dem Konfigurationswert für DATABASE_URL . Es mag überflüssig erscheinen, os.environ["DATABASE_URL"] = config.DATABASE_URL da DATABASE_URL bereits als externe Umgebungsvariable definiert ist. Dies erlaubt pydantic jedoch, diese Variable zu parsen, zu prüfen und zu validieren. Wenn die Umgebungsvariable DATABASE_URL fehlt oder falsch formatiert ist, gibt pydantic eine eindeutige Fehlermeldung aus. Diese Fehlerprüfungen sind von unschätzbarem Wert, wenn die Anwendung von der Entwicklung in nachfolgende Umgebungen übergeht.

Wenn eine Variable nicht definiert ist, wird entweder ein Standardwert zugewiesen oder ein Fehler fordert dazu auf, sie zu definieren. Alle generierten Eingabeaufforderungen geben auch den gewünschten Variablentyp an. Ein Nebeneffekt dieser Prüfungen ist, dass neue Teammitglieder und DevOps-Ingenieure leichter erkennen, welche Variablen definiert werden müssen. Dadurch werden die schwer zu findenden Probleme vermieden, die entstehen, wenn die Anwendung ohne alle definierten Variablen ausgeführt wird.

Das ist es. Die Anwendung verfügt jetzt über eine wartbare Implementierung zur Verwaltung von Einstellungen, die eine einzelne Version von settings.py . Das Schöne an diesem Ansatz ist, dass er es uns ermöglicht, die richtigen Umgebungsvariablen in einer .env -Datei oder jedem anderen gewünschten Mittel anzugeben, das über unsere Hosting-Umgebung verfügbar ist.

Der skalierbare Pfad

Ich habe die Django-Einstellungsverwaltung mit pydantic-Laufzeiteingabe in allen meinen Django-Projekten verwendet. Ich habe festgestellt, dass dies zu kleineren, besser wartbaren Codebasen führt. Es bietet auch einen gut strukturierten, selbstdokumentierenden und skalierbaren Ansatz zum Hinzufügen neuer Umgebungen.

Der nächste Artikel in dieser Reihe ist eine Schritt-für-Schritt-Anleitung zum Erstellen einer Django-Anwendung von Grund auf neu, mit pydantic-Einstellungsverwaltung und deren Bereitstellung in Heroku.


Der Toptal Engineering Blog dankt Stephen Davidson für die Überprüfung der in diesem Artikel vorgestellten Codebeispiele.

In einer früheren Version dieses Artikels wurde eine bestimmte Python-Version hervorgehoben. Da Python seit mehreren Jahren Typhinweise unterstützt, haben wir den Einführungstext verallgemeinert, indem wir diesen Versionsverweis entfernt haben.