Rationalisez vos paramètres Django avec des conseils de type : un didacticiel Pydantic
Publié: 2022-07-22Les projets Django me frustraient parce qu'il me manquait un moyen robuste et évolutif d'ajouter de nouveaux environnements. En réunissant des conseils de type pydantic et Python, j'ai construit la base puissante dont j'avais besoin.
Comme décrit dans la PEP 484, les indications de type prennent en charge l'analyse statique, mais ces mêmes annotations sont également disponibles au moment de l'exécution. Des packages tiers tels que pydantic offrent une vérification du type d'exécution qui utilise ces métadonnées supplémentaires. Pydantic utilise des conseils de type Python pour aider à gérer les métadonnées des paramètres et effectuer la validation des données d'exécution.
Ce didacticiel pydantic montrera les effets positifs considérables de l'utilisation de la gestion des paramètres pydantic avec Django.
Notre configuration respecte les meilleures pratiques décrites sur le site Web de l'application Twelve-Factor :
- Définissez des configurations non constantes et secrètes en tant que variables d'environnement.
- Dans les environnements de développement, définissez les variables d'environnement dans un fichier
.env
et ajoutez le.env
à.gitignore
. - Utilisez les mécanismes du fournisseur de cloud pour définir des variables d'environnement (secrètes) pour les environnements d'assurance qualité, de préproduction et de production.
- Utilisez un seul fichier
settings.py
qui se configure à partir des variables d'environnement. - Utilisez pydantic pour lire, vérifier, valider et transtyper les variables d'environnement en variables Python qui définissent les configurations Django.
Alternativement, certains développeurs créent plusieurs fichiers de paramètres, comme settings_dev.py
et settings_prod.py
. Malheureusement, cette approche ne s'adapte pas bien. Cela entraîne une duplication de code, de la confusion, des bogues difficiles à trouver et des efforts de maintenance plus importants.
En utilisant les meilleures pratiques susmentionnées, l'ajout d'un nombre quelconque d'environnements est simple, bien défini et sans erreur. Bien que nous puissions explorer une configuration d'environnement plus compliquée, nous nous concentrerons sur deux pour plus de clarté : le développement et la production.
À quoi cela ressemble-t-il en pratique ?
Gestion des paramètres Pydantic et variables d'environnement
Nous nous concentrons maintenant sur un exemple en développement et en production. Nous montrons comment chaque environnement configure ses paramètres différemment et comment pydantic les prend en charge.
Notre exemple d'application nécessite une base de données prise en charge par Django, nous devons donc stocker la chaîne de connexion à la base de données. Nous déplaçons les informations de configuration de la connexion à la base de données dans une variable d'environnement, DATABASE_URL
, à l'aide du package Python dj-database-url. Veuillez noter que cette variable est de type str
et est formatée comme suit :
postgres://{user}:{password}@{hostname}:{port}/{database-name} mysql://{user}:{password}@{hostname}:{port}/{database-name} oracle://{user}:{password}@{hostname}:{port}/{database-name} sqlite:///PATH
Dans notre environnement de développement, nous pouvons utiliser une instance PostgreSQL contenue dans Docker pour une facilité d'utilisation, tandis que dans notre environnement de production, nous pointons vers un service de base de données provisionné.
Une autre variable que nous voulons définir est un booléen, DEBUG
. L'indicateur DEBUG dans Django ne doit jamais être activé dans un déploiement de production. Il est destiné à obtenir des commentaires supplémentaires au cours du développement. Par exemple, en mode débogage, Django affichera des pages d'erreur détaillées lorsqu'une exception se produit.
Différentes valeurs pour le développement et la production pourraient être définies comme suit :
Nom de variable | Développement | Production |
---|---|---|
DATABASE_URL | postgres://postgres:mypw@localhost:5432/mydb | postgres://foo1:foo2@foo3:5432/foo4 |
DEBUG | True | False |
Nous utilisons le module de gestion des paramètres pydantic pour gérer ces différents ensembles de valeurs de variables d'environnement en fonction de l'environnement.
Étapes préparatoires
Pour mettre cela en pratique, nous commençons à configurer notre environnement de développement en créant notre unique fichier .env
avec ce contenu :
DATABASE_URL=postgres://postgres:mypw@localhost:5432/mydb DEBUG=True
Ensuite, nous ajoutons le fichier .env
au fichier .gitignore
du projet. Le fichier .gitignore
évite d'enregistrer des informations potentiellement sensibles dans le contrôle de code source.
Alors que cette approche fonctionne bien dans notre environnement de développement, notre spécification d'environnement de production utilise un mécanisme différent. Nos meilleures pratiques dictent que les variables d'environnement de production utilisent des secrets d'environnement. Par exemple, sur Heroku, ces secrets sont appelés Config Vars et sont configurés via le tableau de bord Heroku. Elles sont mises à la disposition de l'application déployée en tant que variables d'environnement :
Après cela, nous devons ajuster la configuration de l'application pour lire automatiquement ces valeurs à partir de l'un ou l'autre des environnements.
Configurer le fichier settings.py
de Django
Commençons par un nouveau projet Django pour fournir la structure essentielle de notre exemple. Nous échafaudons un nouveau projet Django avec la commande de terminal suivante :
$ django-admin startproject mysite
Nous avons maintenant une structure de projet de base pour le projet mysite
. La structure des fichiers du projet est la suivante :
mysite/ manage.py mysite/ __init__.py settings.py urls.py asgi.py wsgi.py
Le fichier settings.py
contient du code passe-partout qui nous permet de gérer la configuration de l'application. Il a de nombreux paramètres par défaut prédéfinis que nous devons ajuster à l'environnement concerné.
Pour gérer ces paramètres d'application à l'aide de variables d'environnement et de pydantic, ajoutez ce code en haut du fichier 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
Ce code effectue les opérations suivantes :
- Définit une classe
SettingsFromEnvironment
, héritant de la classe BaseSettings deBaseSettings
. - Définit
DATABASE_URL
etDEBUG
, en définissant leur type et leur valeur par défaut facultative à l'aide des indications de type Python. - Définit une classe
Config
indiquant à pydantic de rechercher les variables dans un fichier.env
si elles ne sont pas présentes dans les variables d'environnement du système. - Instancie la classe
Config
dans l'objetconfig
; les variables souhaitées deviennent disponibles en tant queconfig.DATABASE_URL
etconfig.DEBUG
. - Définit les variables régulières de Django
DATABASES
etDEBUG
à partir de ces membres deconfig
.
Le même code s'exécute dans tous les environnements, et pydantic s'occupe de ce qui suit :
- Il recherche les variables d'environnement
DATABASE_URL
etDEBUG
.- S'il est défini comme des variables d'environnement, comme en production, il les utilisera.
- Sinon, il extrait ces valeurs du fichier
.env
. - S'il ne trouve pas de valeur, il effectuera les opérations suivantes :
- Pour
DATABASE_URL
, il génère une erreur. - Pour
DEBUG
, il attribue une valeur par défaut deFalse
.
- Pour
- S'il trouve une variable d'environnement, il vérifiera les types de champs et affichera une erreur si l'un d'eux est erroné :
- Pour
DATABASE_URL
, il vérifie que son type de champ est une URL de stylePostgresDsn
. - Pour
DEBUG
, il vérifie que son type de champ est un booléen pydantic valide et non strict.
- Pour
Veuillez noter le paramètre explicite de la variable d'environnement du système d'exploitation à partir de la valeur de configuration de DATABASE_URL
. Il peut sembler redondant de définir os.environ["DATABASE_URL"] = config.DATABASE_URL
car DATABASE_URL
est déjà défini comme une variable d'environnement externe. Cependant, cela permet à pydantic d'analyser, de vérifier et de valider cette variable. Si la variable d'environnement DATABASE_URL
est manquante ou mal formatée, pydantic affichera un message d'erreur clair. Ces vérifications d'erreurs sont inestimables lorsque l'application passe du développement aux environnements suivants.
Si une variable n'est pas définie, soit une valeur par défaut sera affectée, soit une erreur demandera sa définition. Toutes les invites générées détaillent également le type de variable souhaité. Un avantage secondaire de ces vérifications est que les nouveaux membres de l'équipe et les ingénieurs DevOps découvrent plus facilement quelles variables doivent être définies. Cela évite les problèmes difficiles à trouver qui surviennent lorsque l'application s'exécute sans que toutes les variables soient définies.
C'est ça. L'application dispose désormais d'une implémentation maintenable de gestion des paramètres à l'aide d'une seule version de settings.py
. La beauté de cette approche est qu'elle nous permet de spécifier les variables d'environnement correctes dans un fichier .env
ou tout autre moyen souhaité disponible via notre environnement d'hébergement.
Le chemin évolutif
J'utilise la gestion des paramètres Django avec le typage d'exécution pydantic dans tous mes projets Django. J'ai trouvé que cela conduisait à des bases de code plus petites et plus maintenables. Il fournit également une approche bien structurée, auto-documentée et évolutive pour ajouter de nouveaux environnements.
Le prochain article de cette série est un didacticiel étape par étape sur la création d'une application Django à partir de zéro, avec une gestion des paramètres pydantic, et son déploiement sur Heroku.
Le blog Toptal Engineering exprime sa gratitude à Stephen Davidson pour avoir examiné les exemples de code présentés dans cet article.
Une version antérieure de cet article mettait l'accent sur une version spécifique de Python. Étant donné que Python prend en charge les indications de type depuis plusieurs années, nous avons généralisé le texte d'introduction en supprimant cette référence de version.