Simplificați setările Django cu indicații de tip: un tutorial Pydantic
Publicat: 2022-07-22Proiectele Django mă frustrau pentru că îmi lipsea o modalitate robustă și scalabilă de a adăuga noi medii. Reunind indicii de tip pydantic și Python, am construit fundația puternică de care aveam nevoie.
După cum este descris în PEP 484, sugestiile de tip acceptă analiza statică, dar aceleași adnotări sunt disponibile și în timpul execuției. Pachetele terțe precum pydantic oferă verificarea tipului de rulare care utilizează aceste metadate suplimentare. Pydantic folosește indicii de tip Python pentru a ajuta la gestionarea metadatelor setărilor și pentru a efectua validarea datelor de rulare.
Acest tutorial pydantic va arăta efectele pozitive de anvergură ale utilizării gestionării setărilor pydantic cu Django.
Configurația noastră respectă cele mai bune practici descrise pe site-ul web al aplicației Twelve-Factor:
- Definiți configurațiile nonconstante și secrete ca variabile de mediu.
- În mediile de dezvoltare, definiți variabilele de mediu într-un fișier
.env
și adăugați.env
la.gitignore
. - Utilizați mecanismele furnizorului de cloud pentru a defini variabilele de mediu (secrete) pentru mediile QA, staging și producție.
- Utilizați un singur fișier
settings.py
care se configurează singur din variabilele de mediu. - Utilizați pydantic pentru a citi, verifica, valida și tipifica variabilele de mediu pe variabilele Python care definesc configurațiile Django.
Ca alternativă, unii dezvoltatori creează mai multe fișiere de setări, cum ar fi settings_dev.py
și settings_prod.py
. Din păcate, această abordare nu se extinde bine. Aceasta duce la duplicarea codului, confuzie, erori greu de găsit și eforturi mai mari de întreținere.
Folosind cele mai bune practici menționate mai sus, adăugarea oricărui număr de medii este ușoară, bine definită și fără erori. Deși am putea explora o configurație de mediu mai complicată, ne vom concentra pe două pentru claritate: dezvoltare și producție.
Cum arată asta în practică?
Gestionarea setărilor Pydantic și variabilele de mediu
Acum ne concentrăm pe un exemplu atât în dezvoltare, cât și în producție. Arătăm cum fiecare mediu își configurează setările în mod diferit și cum pydantic le acceptă pe fiecare.
Exemplul nostru de aplicație necesită o bază de date acceptată de Django, așa că trebuie să stocăm șirul de conexiune la baza de date. Mutăm informațiile de configurare a conexiunii la baza de date într-o variabilă de mediu, DATABASE_URL
, folosind pachetul Python dj-database-url. Vă rugăm să rețineți că această variabilă este de tip str
și este formatată după cum urmează:
postgres://{user}:{password}@{hostname}:{port}/{database-name} mysql://{user}:{password}@{hostname}:{port}/{database-name} oracle://{user}:{password}@{hostname}:{port}/{database-name} sqlite:///PATH
În mediul nostru de dezvoltare, putem folosi o instanță PostgreSQL conținută în Docker pentru ușurință în utilizare, în timp ce în mediul nostru de producție, vom indica un serviciu de bază de date furnizat.
O altă variabilă pe care vrem să o definim este o booleană, DEBUG
. Indicatorul DEBUG din Django nu trebuie să fie niciodată activat într-o implementare de producție. Este destinat să obțină feedback suplimentar în timpul dezvoltării. De exemplu, în modul de depanare, Django va afișa pagini detaliate de eroare atunci când apare o excepție.
Diferite valori pentru dezvoltare și producție ar putea fi definite după cum urmează:
Nume variabilă | Dezvoltare | Productie |
---|---|---|
DATABASE_URL | postgres://postgres:mypw@localhost:5432/mydb | postgres://foo1:foo2@foo3:5432/foo4 |
DEBUG | True | False |
Folosim modulul de gestionare a setărilor pydantic pentru a gestiona aceste seturi diferite de valori ale variabilelor de mediu în funcție de mediu.
Etape pregătitoare
Pentru a pune acest lucru în practică, începem să configuram mediul nostru de dezvoltare prin crearea unui singur fișier .env
cu acest conținut:
DATABASE_URL=postgres://postgres:mypw@localhost:5432/mydb DEBUG=True
Apoi, adăugăm fișierul .env
în fișierul .gitignore
al proiectului. Fișierul .gitignore
evită salvarea informațiilor potențial sensibile în controlul sursei.
În timp ce această abordare funcționează bine în mediul nostru de dezvoltare, specificațiile pentru mediul nostru de producție utilizează un mecanism diferit. Cele mai bune practici dictează că variabilele de mediu de producție utilizează secrete de mediu. De exemplu, pe Heroku, aceste secrete se numesc Config Vars și sunt configurate prin intermediul Heroku Dashboard. Acestea sunt puse la dispoziția aplicației implementate ca variabile de mediu:
După aceea, trebuie să ajustăm configurația aplicației pentru a citi automat aceste valori din oricare mediu.
Configurarea settings.py
Django.py
Să începem cu un nou proiect Django pentru a oferi structura esențială pentru exemplul nostru. Construim un nou proiect Django cu următoarea comandă de terminal:

$ django-admin startproject mysite
Acum avem o structură de bază a proiectului pentru proiectul mysite
. Structura fișierului proiectului este următoarea:
mysite/ manage.py mysite/ __init__.py settings.py urls.py asgi.py wsgi.py
Fișierul settings.py
conține codul standard care ne permite să gestionăm configurația aplicației. Are multe setări implicite predefinite pe care trebuie să le adaptăm la mediul relevant.
Pentru a gestiona aceste setări ale aplicației folosind variabile de mediu și pydantic, adăugați acest cod în partea de sus a fișierului settings.py
:
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
Acest cod face următoarele:
- Definește o clasă
SettingsFromEnvironment
, moștenind din clasa BaseSettings a luiBaseSettings
. - Definește
DATABASE_URL
șiDEBUG
, setându-le tipul și implicit opțional folosind indicii de tip Python. - Definește o clasă
Config
care îi spune lui pydantic să caute variabilele într-un fișier.env
dacă nu sunt prezente în variabilele de mediu ale sistemului. - Instanțiază clasa
Config
în obiectulconfig
; variabilele dorite devin disponibile caconfig.DATABASE_URL
șiconfig.DEBUG
. - Definește variabilele obișnuite Django
DATABASES
șiDEBUG
din acești membri deconfig
.
Același cod rulează în toate mediile, iar pydantic are grijă de următoarele:
- Acesta caută variabilele de mediu
DATABASE_URL
șiDEBUG
.- Dacă sunt definite ca variabile de mediu, ca în producție, le va folosi.
- În caz contrar, extrage acele valori din fișierul
.env
. - Dacă nu găsește o valoare, va face următoarele:
- Pentru
DATABASE_URL
, se afișează o eroare. - Pentru
DEBUG
, atribuie o valoare implicităFalse
.
- Pentru
- Dacă găsește o variabilă de mediu, va verifica tipurile de câmpuri și va da o eroare dacă oricare dintre ele este greșit:
- Pentru
DATABASE_URL
, acesta verifică dacă tipul său de câmp este o adresă URL în stilPostgresDsn
. - Pentru
DEBUG
, se verifică dacă tipul său de câmp este un boolean pidantic valid, nestrict.
- Pentru
Vă rugăm să rețineți setarea explicită a variabilei de mediu a sistemului de operare din valoarea de configurare pentru DATABASE_URL
. Poate părea redundant să setați os.environ["DATABASE_URL"] = config.DATABASE_URL
deoarece DATABASE_URL
este deja definită ca o variabilă de mediu externă. Cu toate acestea, acest lucru îi permite lui pydantic să analizeze, să verifice și să valideze această variabilă. Dacă variabila de mediu DATABASE_URL
lipsește sau este formatată incorect, pydantic va da un mesaj de eroare clar. Aceste verificări ale erorilor sunt neprețuite, deoarece aplicația trece de la dezvoltare la mediile ulterioare.
Dacă o variabilă nu este definită, fie va fi atribuită o variabilă implicită, fie o eroare va solicita să fie definită. Orice prompturi generate detaliază și tipul de variabilă dorit. Un beneficiu secundar al acestor verificări este că noii membri ai echipei și inginerii DevOps descoperă mai ușor care variabile trebuie definite. Acest lucru evită problemele greu de găsit care apar atunci când aplicația rulează fără toate variabilele definite.
Asta e. Aplicația are acum o implementare care poate fi întreținută, de gestionare a setărilor, folosind o singură versiune a settings.py
. Frumusețea acestei abordări este că ne permite să specificăm variabilele de mediu corecte într-un fișier .env
sau orice alt mijloc dorit disponibil prin mediul nostru de găzduire.
Calea scalabilă
Am folosit managementul setărilor Django cu tastarea pydantic runtime în toate proiectele mele Django. Am descoperit că duce la baze de cod mai mici și mai ușor de întreținut. De asemenea, oferă o abordare bine structurată, auto-documentată și scalabilă pentru adăugarea de noi medii.
Următorul articol din această serie este un tutorial pas cu pas despre construirea unei aplicații Django de la zero, cu gestionarea pidantică a setărilor și implementarea acesteia în Heroku.
Blogul Toptal Engineering își exprimă recunoștința lui Stephen Davidson pentru revizuirea exemplelor de cod prezentate în acest articol.
O versiune anterioară a acestui articol a subliniat o ediție specifică Python. Deoarece Python a acceptat indicii de tip de câțiva ani, am generalizat textul introductiv eliminând această referință de versiune.