Rationalisez vos paramètres Django avec des conseils de type : un didacticiel Pydantic

Publié: 2022-07-22

Les 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 :

  1. Définissez des configurations non constantes et secrètes en tant que variables d'environnement.
  2. Dans les environnements de développement, définissez les variables d'environnement dans un fichier .env et ajoutez le .env à .gitignore .
  3. 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.
  4. Utilisez un seul fichier settings.py qui se configure à partir des variables d'environnement.
  5. 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 :

Une capture d'écran de l'interface Web de Config Vars. La barre latérale de gauche contient une description : "Les variables de configuration modifient le comportement de votre application. En plus de créer les vôtres, certains modules complémentaires sont livrés avec les leurs." La section principale comporte deux lignes, chacune avec deux champs de texte, une icône en forme de crayon et une icône X. Les champs de texte ont les mêmes données que les colonnes "Nom de la variable" et "Production" du tableau précédent. Le coin supérieur droit comporte un bouton indiquant "Masquer les variables de configuration".

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 de BaseSettings .
  • Définit DATABASE_URL et DEBUG , 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'objet config ; les variables souhaitées deviennent disponibles en tant que config.DATABASE_URL et config.DEBUG .
  • Définit les variables régulières de Django DATABASES et DEBUG à partir de ces membres de config .

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 et DEBUG .
    • 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 de False .
  • 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 style PostgresDsn .
    • Pour DEBUG , il vérifie que son type de champ est un booléen pydantic valide et non strict.

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.