gwift-book/source/part-1-workspace/django/_index.adoc

34 KiB
Raw Blame History

Démarrer un nouveau projet

Travailler en isolation

Nous allons aborder la gestion et lisolation des dépendances. Cette section est aussi utile pour une personne travaillant seule, que pour transmettre les connaissances à un nouveau membre de léquipe ou pour déployer lapplication elle-même.

Il en était déjà question au deuxième point des 12 facteurs: même dans le cas de petits projets, il est déconseillé de sen passer. Cela évite les déploiements effectués à larrache à grand renfort de sudo et dinstallation globale de dépendances, pouvant potentiellement occasioner des conflits entre les applications déployées:

  1. Il est tout à fait envisagable que deux applications différentes soient déployées sur un même hôte, et nécessitent chacune deux versions différentes dune même dépendance.

  2. Pour la reproductibilité dun environnement spécifique, cela évite notamment les réponses type "Ca juste marche chez moi", puisque la construction dun nouvel environnement fait partie intégrante du processus de construction et de la documentation du projet; grâce à elle, nous avons la possibilité de construire un environnement sain et dappliquer des dépendances identiques, quelle que soit la machine hôte.

it works on my machine

Dans la suite de ce chapitre, nous allons considérer deux projets différents:

  1. Gwift, une application permettant de gérer des listes de souhaits

  2. Khana, une application de suivi dapprentissage pour des élèves ou étudiants.

Roulements de versions

Django fonctionne sur un roulement de trois versions mineures pour une version majeure, clôturé par une version LTS (Long Term Support).

django support lts

La version utilisée sera une bonne indication à prendre en considération pour nos dépendances, puisquen visant une version particulière, nous ne devrons pratiquement pas nous soucier (bon, un peu quand même, mais nous le verrons plus tard…) des dépendances à installer, pour peu que lon reste sous un certain seuil.

Dans les étapes ci-dessous, nous épinglerons une version LTS afin de nous assurer une certaine sérénité desprit (= dont nous ne occuperons pas pendant les 3 prochaines années).

Environnements virtuels

Un des reproches que lon peut faire au langage concerne sa versatilité: il est possible de réaliser beaucoup de choses, mais celles-ci ne sont pas toujours simples ou directes. Pour quelquun qui débarquererait, la quantité doptions différentes peut paraître rebutante. Nous pensons notamment aux environnements virtuels: ils sont géniaux à utiliser, mais on est passé par virtualenv (lancêtre), virtualenvwrapper (sa version améliorée et plus ergonomique), venv (la version intégrée depuis la version 3.3 de linterpréteur, et la manière recommandée de créer un environnement depuis la 3.5).

Pour créer un nouvel environnement, vous aurez donc besoin:

  1. Dune installation de Python - https://www.python.org/

  2. Dun terminal - voir le point Un terminal

Note
Il existe plusieurs autres modules permettant darriver au même résultat, avec quelques avantages et inconvénients pour chacun dentre eux. Le plus prometteur dentre eux est Poetry, qui dispose dune interface en ligne de commande plus propre et plus moderne que ce que PIP propose.

Poetry se propose de gérer le projet au travers dun fichier pyproject.toml. TOML (du nom de son géniteur, Tom Preston-Werner, légèrement CEO de GitHub à ses heures), se place comme alternative aux formats comme JSON, YAML ou INI.

La commande poetry new <project> créera une structure par défaut relativement compréhensible:

$ poetry new django-gecko
$ tree django-gecko/
django-gecko/
├── django_gecko
│   └── __init__.py
├── pyproject.toml
├── README.rst
└── tests
    ├── __init__.py
    └── test_django_gecko.py

2 directories, 5 files

Ceci signifie que nous avons directement (et de manière standard):

  • Un répertoire django-gecko, qui porte le nom de lapplication que vous venez de créer

  • Un répertoires tests, libellé selon les standards de pytest

  • Un fichier README.rst (qui ne contient encore rien)

  • Un fichier pyproject.toml, qui contient ceci:

[tool.poetry]
name = "django-gecko"
version = "0.1.0"
description = ""
authors = ["... <...@grimbox.be>"]

[tool.poetry.dependencies]
python = "^3.9"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

La commande poetry init permet de générer interactivement les fichiers nécessaires à son intégration dans un projet existant.

Note
Jai pour habitude de conserver mes projets dans un répertoire ~/Sources/ et mes environnements virtuels dans un répertoire ~/.venvs/.

Cette séparation évite que lenvironnement virtuel ne se trouve dans le même répertoire que les sources, ou ne soit accidentellement envoyé vers le système de gestion de versions. Elle évite également de rendre ce répertoire "visible" - il ne sagit au fond que dun paramètre de configuration lié uniquement à votre environnement de développement; les environnements virtuels étant disposables, il nest pas conseillé de trop les lier au projet qui lutilise comme base. Dans la suite de ce chapitre, je considérerai ces mêmes répertoires, mais nhésitez pas à les modifier.

DANGER: Indépendamment de lendroit où vous stockerez le répertoire contenant cet environnement, il est primordial de ne pas le conserver dans votre dépôt de stockager. Cela irait à lencontre des douze facteurs, cela polluera inutilement vos sources et créera des conflits avec lenvironnement des personnes qui souhaiteraient intervenir sur le projet.

Pur créer notre répertoire de travail et notre environnement virtuel, exécutez les commandes suivantes:

mkdir ~/.venvs/
python -m venv ~/.venvs/gwift-venv

Ceci aura pour effet de créer un nouveau répertoire (~/.venvs/gwift-env/), dans lequel vous trouverez une installation complète de linterpréteur Python. Votre environnement virtuel est prêt, il ny a plus quà indiquer que nous souhaitons lutiliser, grâce à lune des commandes suivantes:

# GNU/Linux, macOS
source ~/.venvs/gwift-venv/bin/activate

# MS Windows, avec Cmder
~/.venvs/gwift-venv/Scripts/activate.bat

# Pour les deux
(gwift-env) fred@aerys:~/Sources/.venvs/gwift-env$ (1)
  1. Le terminal signale que nous sommes bien dans lenvironnement gwift-env.

A présent que lenvironnement est activé, tous les binaires de cet environnement prendront le pas sur les binaires du système. De la même manière, une variable PATH propre est définie et utilisée, afin que les librairies Python y soient stockées. Cest donc dans cet environnement virtuel que nous retrouverons le code source de Django, ainsi que des librairies externes pour Python une fois que nous les aurons installées.

Note
Pour les curieux, un environnement virtuel nest jamais quun répertoire dans lequel se trouve une installation fraîche de linterpréteur, vers laquelle pointe les liens symboliques des binaires. Si vous recherchez lemplacement de linterpréteur avec la commande which python, vous recevrez comme réponse /home/fred/.venvs/gwift-env/bin/python.

Pour sortir de lenvironnement virtuel, exécutez la commande deactivate. Si vous pensez ne plus en avoir besoin, supprimer le dossier. Si nécessaire, il suffira den créer un nouveau.

Pour gérer des versions différentes dune même librairie, il nous suffit de jongler avec autant denvironnements que nécessaires. Une application nécessite une version de Django inférieure à la 2.0 ? On crée un environnement, on lactive et on installe ce quil faut.

Cette technique fonctionnera autant pour un poste de développement que sur les serveurs destinés à recevoir notre application.

Note
Par la suite, nous considérerons que lenvironnement virtuel est toujours activé, même si gwift-env nest pas indiqué.

a manière recommandée pour la gestion des dépendances consiste à les épingler dans un fichier requirements.txt, placé à la racine du projet. Ce fichier reprend, ligne par ligne, chaque dépendance et la version nécessaire. Cet épinglage est cependant relativement basique, dans la mesure où les opérateurs disponibles sont ==, ⇐ et >=.

Poetry propose un épinglage basé sur SemVer. Les contraintes qui peuvent être appliquées aux dépendances sont plus touffues que ce que proposent pip -r, avec la présence du curseur ^, qui ne modifiera pas le nombre différent de zéro le plus à gauche:

^1.2.3 (où le nombre en question est 1) pourra proposer une mise à jour jusqu'à la version juste avant la version 2.0.0
^0.2.3 pourra être mise à jour jusqu'à la version juste avant 0.3.0.
...

Lavantage est donc que lon spécifie une version majeure - mineure - patchée, et que lon pourra spécifier accepter toute mise à jour jusquà la prochaine version majeure - mineure patchée (non incluse 😉).

Une bonne pratique consiste également, tout comme pour npm, à intégrer le fichier de lock (poetry.lock) dans le dépôt de sources: de cette manière, seules les dépendances testées (et intégrées) seront considérées sur tous les environnements de déploiement.

Il est alors nécessaire de passer par une action manuelle (poetry update) pour mettre à jour le fichier de verrou, et assurer une mise à jour en sécurité (seules les dépendances testées sont prises en compte) et de qualité (tous les environnements utilisent la même version dune dépendance).

Lajout dune nouvelle dépendance à un projet se réalise grâce à la commande poetry add <dep>:

$ poetry add django
Using version ^3.2.3 for Django

Updating dependencies
Resolving dependencies... (5.1s)

Writing lock file

Package operations: 8 installs, 1 update, 0 removals

  • Installing pyparsing (2.4.7)
  • Installing attrs (21.2.0)
  • Installing more-itertools (8.8.0)
  • Installing packaging (20.9)
  • Installing pluggy (0.13.1)
  • Installing py (1.10.0)
  • Installing wcwidth (0.2.5)
  • Updating django (3.2 -> 3.2.3)
  • Installing pytest (5.4.3)

Elle est ensuite ajoutée à notre fichier pyproject.toml:

[...]

[tool.poetry.dependencies]
python = "^3.9"
Django = "^3.2.3"

[...]

Et contrairement à pip, pas besoin de savoir sil faut pointer vers un fichier (-r) ou un dépôt VCS (-e), puisque Poetry va tout essayer, [dans un certain ordre](https://python-poetry.org/docs/cli/#add). Lavantage également (et cela marrive encore souvent, ce qui fait hurler le runner de Gitlab), cest quil nest plus nécessaire de penser à épingler la dépendance que lon vient dinstaller parmi les fichiers de requirements, puisquelles sy ajoutent automatiquement grâce à la commande add.

Python packaging made easy

Cette partie dépasse mes compétences et connaissances, dans la mesure où je nai jamais rien packagé ni publié sur [pypi.org](pypi.org). Ce nest pas lenvie qui manque, mais les idées et la nécessité 😉. Ceci dit, Poetry propose un ensemble de règles et une préconfiguration qui (doivent) énormément facilite(r) la mise à disposition de librairies sur Pypi - et rien que ça, devrait ouvrir une partie de lécosystème.

Les chapitres 7 et 8 de [Expert Python Programming - Third Edtion](#), écrit par Michal Jaworski et Tarek Ziadé en parlent très bien:

Python packaging can be a bit overwhelming at first. The main reason for that is the confusion about proper tools for creating Python packages. Anyway, once you create your first package, you will se that this is as hard as it looks. Also, knowing propre, state-of-the-art packaging helps a lot.

En gros, cest ardu-au-début-mais-plus-trop-après. Et cest heureusement suivi et documenté par la PyPA (Python Packaging Authority).

Les étapes sont les suivantes:

  1. Utiliser setuptools pour définir les projets et créer les distributions sources,

  2. Utiliser wheels pour créer les paquets,

  3. Passer par twine pour envoyer ces paquets vers PyPI

  4. Définir un ensemble dactions (voire, de plugins nécessaires - lien avec le VCS, etc.) dans le fichier setup.py, et définir les propriétés du projet ou de la librairie dans le fichier setup.cfg.

Avec Poetry, deux commandes suffisent (théoriquement - puisque je nai pas essayé 🤪): poetry build et poetry publish:

$ poetry build
Building geco (0.1.0)
  - Building sdist
  - Built geco-0.1.0.tar.gz
  - Building wheel
  - Built geco-0.1.0-py3-none-any.whl

$ tree dist/
dist/
├── geco-0.1.0-py3-none-any.whl
└── geco-0.1.0.tar.gz

0 directories, 2 files

Ce qui est quand même 'achement plus simple que dappréhender tout un écosystème.

Gestion des dépendances, installation de Django et création dun nouveau projet

Comme nous en avons déjà discuté, PIP est la solution que nous avons choisie pour la gestion de nos dépendances. Pour installer une nouvelle librairie, vous pouvez simplement passer par la commande pip install <my_awesome_library>. Dans le cas de Django, et après avoir activé lenvironnement, nous pouvons à présent y installer Django. Comme expliqué ci-dessus, la librairie restera indépendante du reste du système, et ne polluera aucun autre projet. nous exécuterons donc la commande suivante:

$ source ~/.venvs/gwift-env/bin/activate # ou ~/.venvs/gwift-env/Scrips/activate.bat pour Windows.
$ pip install django
Collecting django
    Downloading Django-3.1.4
100% |################################|
Installing collected packages: django
Successfully installed django-3.1.4
Important
Ici, la commande pip install django récupère la dernière version connue disponible dans les dépôts https://pypi.org/ (sauf si vous en avez définis dautres. Mais cest hors sujet). Nous en avons déjà discuté: il est important de bien spécifier la version que vous souhaitez utiliser, sans quoi vous risquez de rencontrer des effets de bord.

Linstallation de Django a ajouté un nouvel exécutable: django-admin, que lon peut utiliser pour créer notre nouvel espace de travail. Par la suite, nous utiliserons manage.py, qui constitue un wrapper autour de django-admin.

Pour démarrer notre projet, nous lançons django-admin startproject gwift:

$ django-admin startproject gwift

Cette action a pour effet de créer un nouveau dossier gwift, dans lequel nous trouvons la structure suivante:

$ tree gwift
gwift
├── gwift
|   |── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

Cest dans ce répertoire que vont vivre tous les fichiers liés au projet. Le but est de faire en sorte que toutes les opérations (maintenance, déploiement, écriture, tests, …​) puissent se faire à partir dun seul point dentrée.

Lutilité de ces fichiers est définie ci-dessous:

  • settings.py contient tous les paramètres globaux à notre projet.

  • urls.py contient les variables de routes, les adresses utilisées et les fonctions vers lesquelles elles pointent.

  • manage.py, pour toutes les commandes de gestion.

  • asgi.py contient la définition de linterface ASGI, le protocole pour la passerelle asynchrone entre votre application et le serveur Web.

  • wsgi.py contient la définition de linterface WSGI, qui permettra à votre serveur Web (Nginx, Apache, …​) de faire un pont vers votre projet.

Note
Indiquer quil est possible davoir plusieurs structures de dossiers et quil ny a pas de "magie" derrière toutes ces commandes.

Tant que nous y sommes, nous pouvons ajouter un répertoire dans lequel nous stockerons les dépendances et un fichier README:

(gwift) $ mkdir requirements
(gwift) $ touch README.md
(gwift) $ tree gwift
gwift
├── gwift
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── requirements (1)
├── README.md (2)
└── manage.py
  1. Ici

  2. Et là

Comme nous venons dajouter une dépendance à notre projet, profitons-en pour créer un fichier reprenant tous les dépendances de notre projet. Celles-ci sont normalement placées dans un fichier requirements.txt. Dans un premier temps, ce fichier peut être placé directement à la racine du projet, mais on préférera rapidement le déplacer dans un sous-répertoire spécifique (requirements), afin de grouper les dépendances en fonction de leur environnement de destination:

  • base.txt

  • dev.txt

  • production.txt

Au début de chaque fichier, il suffit dajouter la ligne -r base.txt, puis de lancer linstallation grâce à un pip install -r <nom du fichier>. De cette manière, il est tout à fait acceptable de ninstaller flake8 et django-debug-toolbar quen développement par exemple. Dans limmédiat, nous allons ajouter django dans une version strictement inférieure à la version 3.2 dans le fichier requirements/base.txt.

$ echo 'django==3.2' > requirements/base.txt
$ echo '-r base.txt' > requirements/prod.txt
$ echo '-r base.txt' > requirements/dev.txt
Important
Prenez directement lhabitude de spécifier la version ou les versions compatibles: les librairies que vous utilisez comme dépendances évoluent, de la même manière que vos projets. Pour être sûr et certain le code que vous avez écrit continue à fonctionner, spécifiez la version de chaque librairie de dépendances. Entre deux versions dune même librairie, des fonctions sont cassées, certaines signatures sont modifiées, des comportements sont altérés, etc. Il suffit de parcourir les pages de Changements incompatibles avec les anciennes versions dans Django (par exemple ici pour le passage de la 3.0 à la 3.1) pour réaliser que certaines opérations ne sont pas anodines, et que sans filet de sécurité, cest le mur assuré. Avec les mécanismes dintégration continue et de tests unitaires, nous verrons plus loin comment se prémunir dun changement inattendu.

Django

Comme nous lavons vu ci-dessus, django-admin permet de créer un nouveau projet. Nous faisons ici une distinction entre un projet et une application:

  • Un projet représente lensemble des applications, paramètres, pages HTML, middlewares, dépendances, etc., qui font que votre code fait ce quil est sensé faire.

  • Une application est un contexte dexécution, idéalement autonome, dune partie du projet.

Pour gwift, nous aurons:

django project vs apps gwift
Figure 2. Django Projet vs Applications
  1. une première application pour la gestion des listes de souhaits et des éléments,

  2. une deuxième application pour la gestion des utilisateurs,

  3. voire une troisième application qui gérera les partages entre utilisateurs et listes.

Nous voyons également que la gestion des listes de souhaits et éléments aura besoin de la gestion des utilisateurs - elle nest pas autonome -, tandis que la gestion des utilisateurs na aucune autre dépendance quelle-même.

Pour khana, nous pourrions avoir quelque chose comme ceci:

django project vs apps khana
Figure 3. Django Project vs Applications

En rouge, vous pouvez voir quelque chose que nous avons déjà vu: la gestion des utilisateurs et la possibilité quils auront de communiquer entre eux. Ceci pourrait être commun aux deux applications. Nous pouvons clairement visualiser le principe de contexte pour une application: celle-ci viendra avec son modèle, ses tests, ses vues et son paramétrage et pourrait ainsi être réutilisée dans un autre projet. Cest en ça que consistent les paquets Django déjà disponibles: ce sont "simplement" de petites applications empaquetées et pouvant être réutilisées dans différents contextes (eg. Django-Rest-Framework, Django-Debug-Toolbar, …​).

manage.py

Le fichier manage.py que vous trouvez à la racine de votre projet est un wrapper sur les commandes django-admin. A partir de maintenant, nous nutiliserons plus que celui-là pour tout ce qui touchera à la gestion de notre projet:

  • manage.py check pour vérifier (en surface…) que votre projet ne rencontre aucune erreur évidente

  • manage.py check --deploy, pour vérifier (en surface aussi) que lapplication est prête pour un déploiement

  • manage.py runserver pour lancer un serveur de développement

  • manage.py test pour découvrir les tests unitaires disponibles et les lancer.

La liste complète peut être affichée avec manage.py help. Vous remarquerez que ces commandes sont groupées selon différentes catégories:

  • auth: création dun nouveau super-utilisateur, changer le mot de passe pour un utilisateur existant.

  • django: vérifier la compliance du projet, lancer un shell, dumper les données de la base, effectuer une migration du schéma, …​

  • sessions: suppressions des sessions en cours

  • staticfiles: gestion des fichiers statiques et lancement du serveur de développement.

Nous verrons plus tard comment ajouter de nouvelles commandes.

Si nous démarrons la commande python manage.py runserver, nous verrons la sortie console suivante:

$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

[...]

December 15, 2020 - 20:45:07
Django version 3.1.4, using settings 'gwift.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Si nous nous rendons sur la page http://127.0.0.1:8000 (ou http://localhost:8000) comme le propose si gentiment notre (nouveau) meilleur ami, nous verrons ceci:

manage runserver
Figure 4. python manage.py runserver (Non, ce nest pas Challenger)
Important
Nous avons mis un morceau de la sortie console entre crochet […​] ci-dessus, car elle concerne les migrations. Si vous avez suivi les étapes jusquici, vous avez également dû voir un message type You have 18 unapplied migration(s). […​] Run 'python manage.py migrate' to apply them. Cela concerne les migrations, et cest un point que nous verrons un peu plus tard.

Création dune nouvelle application

Maintenant que nous avons a vu à quoi servait manage.py, nous pouvons créer notre nouvelle application grâce à la commande manage.py startapp <label>.

Notre première application servira à structurer les listes de souhaits, les éléments qui les composent et les parties que chaque utilisateur pourra offrir. De manière générale, essayez de trouver un nom éloquent, court et qui résume bien ce que fait lapplication. Pour nous, ce sera donc wish.

Cest parti pour manage.py startapp wish!

$ python manage.py startapp wish

Résultat? Django nous a créé un répertoire wish, dans lequel nous trouvons les fichiers et dossiers suivants:

  • wish/init.py pour que notre répertoire wish soit converti en package Python.

  • wish/admin.py servira à structurer ladministration de notre application. Chaque information peut être administrée facilement au travers dune interface générée à la volée par le framework. Nous y reviendrons par la suite.

  • wish/apps.py qui contient la configuration de lapplication et qui permet notamment de fixer un nom ou un libellé https://docs.djangoproject.com/en/stable/ref/applications/

  • wish/migrations/ est le dossier dans lequel seront stockées toutes les différentes migrations de notre application (= toutes les modifications que nous apporterons aux données que nous souhaiterons manipuler)

  • wish/models.py représentera et structurera nos données, et est intimement lié aux migrations.

  • wish/tests.py pour les tests unitaires.

Note
Par soucis de clarté, vous pouvez déplacer ce nouveau répertoire wish dans votre répertoire gwift existant. Cest une forme de convention.

La structure de vos répertoires devient celle-ci:

(gwift-env) fred@aerys:~/Sources/gwift$ tree .
.
├── gwift
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   ├── wish (1)
│   │   ├── __init__.py
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── migrations
│   │   │   └── __init__.py
│   │   ├── models.py
│   │   ├── tests.py
│   │   └── views.py
│   └── wsgi.py
├── Makefile
├── manage.py
├── README.md
├── requirements
│   ├── base.txt
│   ├── dev.txt
│   └── prod.txt
├── setup.cfg
└── tox.ini

5 directories, 22 files
  1. Notre application a bien été créée, et nous lavons déplacée dans le répertoire gwift !

Fonctionement général

Le métier de programmeur est devenu de plus en plus complexe. Il y a 20 ans, nous pouvions nous contenter dune simple page PHP dans laquelle nous mixions lensemble des actios à réaliser: requêtes en bases de données, construction de la page, …​ La recherche dune solution a un problème nétait pas spécialement plus complexe - dans la mesure où le rendu des enregistrements en direct nétait finalement quune forme un chouia plus évoluée du print() ou des System.out.println() - mais cétait lévolutivité des applications qui en prenait un coup: une grosse partie des tâches étaient dupliquées entre les différentes pages, et lajout dune nouvelle fonctionnalité était relativement ardue.

Django (et dautres cadriciels) résolvent ce problème en se basant ouvertement sur le principe de Dont repeat yourself [1]. Chaque morceau de code ne doit apparaitre quune seule fois, afin de limiter au maximum la redite (et donc, lapplication dun même correctif à différents endroits).

Le chemin parcouru par une requête est expliqué en (petits) détails ci-dessous.

django how it works
Figure 5. How it works

1. Un utilisateur ou un visiteur souhaite accéder à une URL hébergée et servie par notre application. Ici, nous prenons lexemple de lURL fictive https://gwift/wishes/91827. Lorsque cette URL "arrive" dans notre application, son point dentrée se trouvera au niveau des fichiers asgi.py ou wsgi.py. Nous verrons cette partie plus tard, et nous pouvons nous concentrer sur le chemin interne quelle va parcourir.

Etape 0 - La première étape consiste à vérifier que cette URL répond à un schéma que nous avons défini dans le fichier gwift/urls.py.

Etape 1 - Si ce nest pas le cas, lapplication nira pas plus loin et retournera une erreur à lutilisateur.

Etape 2 - Django va parcourir lensemble des patterns présents dans le fichier urls.py et sarrêtera sur le premier qui correspondra à la requête quil a reçue. Ce cas est relativement trivial: la requête /wishes/91827 a une correspondance au niveau de la ligne path("wishes/<int:wish_id> dans lexemple ci-dessous. Django va alors appeler la fonction [2] associée à ce pattern, cest-à-dire wish_details du module gwift.views.

from django.contrib import admin
from django.urls import path

from gwift.views import wish_details (1)

urlpatterns = [
    path('admin/', admin.site.urls),
    path("wishes/<int:wish_id>", wish_details), (2)
]
  1. Nous importons la fonction wish_details du module gwift.views

  2. Champomy et cotillons! Nous avons une correspondance avec wishes/details/91827

TODO: En fait, il faudrait quand même soccuper du modèle ici. TODO: et de la mise en place de ladministration, parce que nous en aurons besoin pour les étapes de déploiement.

Nous nallons pas nous occuper de laccès à la base de données pour le moment (nous nous en occuperons dans un prochain chapitre) et nous nous contenterons de remplir un canevas avec un ensemble de données.

Le module gwift.views qui se trouve dans le fichier gwift/views.py peut ressembler à ceci:

[...]

from datetime import datetime


def wishes_details(request: HttpRequest, wish_id: int) -> HttpResponse:
    context = {
        "user_name": "Bond,"
        "user_first_name": "James",
        "now": datetime.now()
    }

    return render(
        request,
        "wish_details.html",
        context
    )

Pour résumer, cette fonction permet:

  1. De construire un contexte, qui est représenté sous la forme dun dictionnaire associant des clés à des valeurs. Les clés sont respectivement user_name, user_first_name et now, tandis que leurs valeurs respectives sont Bond, James et le moment présent [3].

  2. Nous passons ensuite ce dictionnaire à un canevas, wish_details.html

  3. Lapplication du contexte sur le canevas nous donne un résultat.

<!-- fichier wish_details.html -->
<!DOCTYPE html>
<html>
<head>
  <title>Page title</title>
</head>
<body>
  <h1>👤 Hi!</h1>
  <p>My name is {{ user_name }}. {{ user_first_name }} {{ user_name }}.</p>
  <p>This page was generated at {{ now }}</p>
</body>
</html>

Après application de notre contexte sur ce template, nous obtiendrons ce document, qui sera renvoyé au navigateur de lutilisateur qui aura fait la requête initiale:

<!DOCTYPE html>
<html>
<head>
  <title>Page title</title>
</head>
<body>
  <h1>👤 Hi!</h1>
  <p>My name is Bond. James Bond.</p>
  <p>This page was generated at 2027-03-19 19:47:38</p>
</body>
</html>
django first template
Figure 6. Résultat

12 facteurs et configuration globale

→ Faire le lien avec les settings → Faire le lien avec les douze facteurs → Construction du fichier setup.cfg

setup.cfg

(Repris de cookie-cutter-django)

[flake8]
max-line-length = 120
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv

[pycodestyle]
max-line-length = 120
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv

[mypy]
python_version = 3.8
check_untyped_defs = True
ignore_missing_imports = True
warn_unused_ignores = True
warn_redundant_casts = True
warn_unused_configs = True
plugins = mypy_django_plugin.main

[mypy.plugins.django-stubs]
django_settings_module = config.settings.test

[mypy-*.migrations.*]
# Django migrations should not produce any errors:
ignore_errors = True

[coverage:run]
include = khana/*
omit = *migrations*, *tests*
plugins =
    django_coverage_plugin

Structure finale de notre environnement

Nous avons donc la structure finale pour notre environnement de travail:

(gwift-env) fred@aerys:~/Sources/gwift$ tree .
.
├── gwift
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   ├── wish (1)
│   │   ├── __init__.py
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── migrations
│   │   │   └── __init__.py
│   │   ├── models.py
│   │   ├── tests.py
│   │   └── views.py
│   └── wsgi.py
├── Makefile
├── manage.py
├── README.md
├── requirements
│   ├── base.txt
│   ├── dev.txt
│   └── prod.txt
├── setup.cfg
└── tox.ini

Pfiou! Ca en fait des commandes et du boulot pour "juste" démarrer un nouveau projet, non? Sachant quen plus, nous avons dû modifier des fichiers, déplacer des dossiers, ajouter des dépendances, configurer une base de données, …​

Bonne nouvelle! Il existe des générateurs, permettant de démarrer rapidement un nouveau projet sans (trop) se prendre la tête. Le plus connu (et le plus personnalisable) est Cookie-Cutter, qui se base sur des canevas type Jinja2, pour créer une arborescence de dossiers et fichiers conformes à votre manière de travailler. Et si vous avez la flemme de créer votre propre canevas, vous pouvez utiliser ceux qui existent déjà.

Pour démarrer, créez un environnement virtuel (comme dhabitude):

λ python -m venv .venvs\cookie-cutter-khana
λ .venvs\cookie-cutter-khana\Scripts\activate.bat
(cookie-cutter-khana) λ pip install cookiecutter

  Collecting cookiecutter
  [...]
  Successfully installed Jinja2-2.11.2 MarkupSafe-1.1.1 arrow-0.17.0 binaryornot-0.4.4 certifi-2020.12.5 chardet-4.0.0 click-7.1.2 cookiecutter-1.7.2 idna-2.10 jinja2-time-0.2.0 poyo-0.5.0 python-dateutil-2.8.1 python-slugify-4.0.1 requests-2.25.1 six-1.15.0 text-unidecode-1.3 urllib3-1.26.2

(cookie-cutter-khana) λ cookiecutter https://github.com/pydanny/cookiecutter-django

  [...]

 [SUCCESS]: Project initialized, keep up the good work!

Si vous explorez les différents fichiers, vous trouverez beaucoup de similitudes avec la configuration que nous vous proposions ci-dessus. En fonction de votre expérience, vous serez tenté de modifier certains paramètres, pour faire correspondre ces sources avec votre utilisation ou vos habitudes.

Note
Il est aussi possible dutiliser largument --template, suivie dun argument reprenant le nom de votre projet (<my_project>), lors de linitialisation dun projet avec la commande startproject de django-admin, afin de calquer votre arborescence sur un projet existant. La documentation à ce sujet est assez complète.
django-admin.py startproject --template=https://[...].zip <my_project>

1. DRY
2. Qui ne sera pas toujours une fonction. Django sattend à trouver un callable, cest-à-dire nimporte quel élément quil peut appeler comme une fonction.
3. Non, pas celui dEckhart Tolle