Une Flask de Django

Bonjour à tous !

Pour ceux qui ont déjà fait du développement web avec Django le titre est évident, pour les autres, bienvenue sur mon introduction à Django !

Django est un excellent framework Python qui permet de développer rapidement des applications web, et qui est très complet. Il est plus long à prendre en main que Flask, un autre microframework Python que je vais utiliser comme exemple afin d'introduire quelques concepts de Django, et de montrer que ces idées bizarres ne sortent pas de nulle part.

Parés ? C'est parti !

Prérequis

Ce tutoriel va référencer des tags du dépôt git à cette adresse. Je vous conseille aussi cet excellent tutoriel qui pourra compléter ce que j'écris ci-dessous. Enfin, la documentation de Django est complète et facile à utiliser.

Je vous conseille donc de cloner le dépôt ci-dessus :

git clone https://git.hashtagueule.fr/motius/django-tutorial.git

et de suivre les tags que j'ai indiqués au fur et à mesure de l'avancement du tutoriel grâce à la commande :

git checkout <tagname>

Je préfixe les noms de tous les tags git de ce tutoriel par un "_", afin que ceux-ci ne polluent pas le projet si vous en réutilisez le dépôt git par la suite (git tag admet l'option -d pour ceux qui veulent vraiment se débarasser de mes tags).

La quasi-totalité de ce tutoriel n'impose pas d'accès root sur le système de développement, je suppose tout de même que vous avez python3 et pip3 d'installés, un accès au réseau pour les packages Python supplémentaires, ainsi que le gestionnaire de versions de code git.

J'utilise une debian GNU/Linux 9.1 Stretch pour le développement, mais ce tutoriel doit facilement être adaptable à d'autres distributions.

Commençons en douceur avec Flask.

Flask, en deux mots

Je vais me faire battre, je vais parler de Flask alors que je l'ai utilisé en tout et pour tout 7 minutes 24 secondes dans ma courte vie.

Installation

Prenons l'exemple le plus basique avec Flask. La documentation du projet se trouve ici.

Elle indique qu'il suffit de taper la commande suivante pour installer Flask :

pip3 install --user Flask

Le projet fil d'ariane

Si vous avez suivi l'étape précédente, vous savez qu'il faut revenir à la version du tag "_v0_flask" comme ceci :

git checkout _v0_flask

pour cette partie du tutoriel. Vous pouvez alors faire tourner le serveur de test à l'aide de la commande :

make

Je vais faire une utilisation intensive des Makefiles, que j'affectionne, mais je vous encourage à les parcourir pour voir ce qu'ils exécutent.

Ici, la commande est simplement :

python3 ma_flasque.py

Vous pouvez alors vous rendre à l'url suivante : http://localhost:5000 dans mon cas.

Le pourquoi du comment

La partie intéressante que je voulais mettre en avant avec cet exemple concerne la structure du code Flask : j'ai divisé le source en trois parties, les imports, les routes Flask, et le code pour faire tourner l'application. La deuxième partie concernant les routes et le code métier est celle qui nous intéresse.

Les routes sont indiquées par un :

@app.route('/', methods=["GET"])

tandis que le code à exécuter dans le cas où l'utilisateur demande ('GET') la page par défaut de l'application ('/') se situe dans le code de la fonction Python index, qui dans notre cas retourne la chaîne de caractères "Hello World!" (tradition oblige) :

def index():
    return "Hello World!"

Ainsi, Flask pose les bases de la programmation avec le motif MVC, que Django utilise : on décrit de manière séparée le code de routage des URL et le code permettant d'obtenir le résultat demandé.

Django utilisera des fichiers séparés pour le routage (Vues) et pour les fonctions Python du Contrôleur, mais le principe est le même.

Il va falloir un peu de travail pour arriver au même résultat avec Django, mais n'aillez pas peur, on y arrivera. L'essentiel est de garder les yeux sur l'objectif : avoir une séparation entre le code de routage et le code métier.

Django va un peu plus loin car il permet de s'interfacer très facilement avec une base de données et en propose une abstraction très propre, il possède un moteur de template pour prémâcher du HTML, permet d'installer des modules, mais tout ça viendra plus tard, je vous mets l'eau à la bouche...

gâteau au chocolat

Un gâteau au chocolat pour vous mettre l'eau à la bouche si ma description des fonctionnalités de Django n'a pas suffi.

Même résultat avec Django

Installation

La page de téléchargement de Django indique, à l'heure actuelle, qu'il suffit de taper la commande suivante pour installer la dernière version de Django :

pip3 install Django==1.11.5

Si vous utilisez Django de manière professionnelle ou amateure, il faut consulter la page de téléchargement pour mettre à jour la version de Django utilisée.

Vous pouvez aussi installer la version de votre distribution, par exemple à l'aide de

su -c 'apt-get install python3-django'

si vous utilisez une debian ou dérivée (Ubuntu, Mint...)

On utilisera Django dans un virtualenv Python pour ce projet.

Virtualenv

Python est un langage de programmation modulaire dont le cœur des fonctionnalités est assez réduit, mais qui reste très puissant grâce au grand écosystème disponible de bibliothèques logicielles, officielles ou tierces.

Celles-ci peuvent être importées dans un script par une primitive import du type :

import os, sys
import re
import numpy as np
from django.http import HttpResponse

etc.

Les virtualenv Python servent à gérer pour chaque projet ses dépendances exactes, à l'inclusion des numéros de version des bibliothèques utilisées, et ce sans interférer avec d'autres projets.

On utilisera donc les virtualenv pour des questions de facilité, et parce qu'ils permettent de s'assurer de la pérennité d'un développement.

Vous pouvez installer virtualenv à l'aide de la commande :

sudo apt-get install python3-virtualenv

ou en tapant :

pip3 install --user virtualenv

Premier projet Django dans un Virtualenv

La version "_v1_django" permet d'installer Django dans un virtualenv Python. Si vous avez installé Django et Virtualenv comme indiqué précédemment, l'installation devrait se faire sans retélécharger des bibliothèques Python depuis internet.

Elle suppose que vous installez toutes les bibliothèques avec pip, si ce n'était pas le cas, il faut éventuellement changer la variable VENV du Makefile, ou installer une version avec pip3.

Pour installer le virtualenv, Django, créer un projet et une application, tapez simplement la commande :

make

Pour l'arrêter, tapez la combinaison de touches CTRL-C.

Détaillons les étapes du Makefile :

  • création du répertoire d'installation de l'environnement de développement, représenté par la variable INSTALL_DIR dans le Makefile ;
  • création du virtualenv Python dans l'environnement de développement ;
  • mise à jour de pip3 dans l'environnement de développement ;
  • installation de Django ;
  • création du projet, dont le nom est représenté par la variable PROJECT_NAME ;
  • création de l'application APP_NAME.

Vous remarquerez que chacune des étapes ne modifie que le répertoire d'installation situé à l'intérieur du répertoire git, jamais des bibliothèques système.

Utilisation de Django

Jusqu'ici, on n'a toujours pas écrit de code correspondant à notre application, seulement décrit une manière générale d'obtenir un projet Django d'équerre pour commencer. Il reste quelques étapes à connaître pour avancer dans le développement d'applications web avec Django sans que celui-ci se mette en travers de notre chemin.

Migrations

Lors de la création d'un projet Django, certaines applications sont installées pour nous, par exemple l'interface d'administration. Celle-ci n'est pas migrée, opération qu'il faut réaliser pour pouvoir l'utiliser.

On a par ailleurs créé notre propre application, qui n'est pas non plus migrée.

Il est nécessaire d'effectuer les migrations des applications, car celles-ci peuvent entraîner une modification du modèle en base de données.

Obtenez le code de la version _v2_migration du projet git pour pouvoir migrer les applications du projet, et lancer le serveur Django de développement.

La commande :

make

permet d'effectuer toutes les étapes décrites jusqu'ici :

  • installation de virtualenv, mise à jour de pip3, installation de django, etc. ;
  • création d'un projet django ;
  • création d'une application django ;
  • migrations du projet et de l'application ;
  • lancement du serveur de développement.

Rendez-vous  à l'URL suivante pour la page par défaut de Django : http://localhost:8000

Organisation du dépôt

Le dépôt est organisé de la manière suivante :

.
+-- doc
│   \-- README.md
+-- exec\_django.sh
+-- LICENSE
+-- Makefile
+-- mon\_django
│   +-- Makefile
│   +-- mon\_app
│   \-- mon\_django
+-- README.md
\-- sandbox
    +-- bin
    +-- db.sqlite3
    +-- include
    +-- lib
    +-- Makefile -> ../mon\_django/Makefile
    +-- manage.py
    +-- mon\_app -> ../mon\_django/mon\_app
    +-- mon\_django -> ../mon\_django/mon\_django
    +-- pip-selfcheck.json
    +-- static
    \-- templates -> ../mon\_django/templates
  • les sources sont dans le répertoire mon_django (nom du projet) ;
  • la production est dans le répertoire sandbox ;
  • les sources ont des liens sympboliques vers la production ;
  • à la racine du projet se retrouvent éventuellement :
    • la documentation ;
    • la licence ;
    • les tests (unitaires, intégration).

Cette organisation a pour but de simplifier le développement :

  • le Makefile (et les scripts qu'il appelle) permet de construire et reconstruire le projet à l'aide d'une seule commande ;
  • à chaque enregistrement d'une modification d'un fichier source sur disque, le serveur de développement Django redémarre avec la mise à jour, grâce aux liens symboliques vers les sources ;
  • la production est séparée des sources ;
  • le dépôt git peut être organisé en suivant les bonnes pratiques de développement Python :
    • disposition de la documentation ;
    • disposition des tests unitaires et intégration...

Comparaison Django et Flask

Ça y est, on a enfin tout un environnement prêt pour le développement. Vous allez me dire "était-ce bien la peine de faire tout ça pour en arriver au même point ?" Je crois que oui.

Avantages de Django

Pour plusieurs raisons :

  • le code de Django est plus organisé que celui de Flask, ce qui procure un avantage sur le long terme :
    • les vues et le contrôleur sont séparés ;
    • Django incite à faire du développement de petites applications réutilisables au sein de plusieurs projets
  • Django va permettre de s'interfacer facilement avec une base de données, ce qui fait qu'on a en fait de l'avance par rapport au cas où l'on serait restés avec Flask
  • j'ai mis en place un système de construction avec les Makefiles qui permet de tester son application à l'aide d'une seule commande, comme pour Flask.

Je ne détaille pas les inconvénients de Django, il y en a un principal selon moi, la relative difficulté de le mettre en place, ce qui est fait par le système de construction logicielle avec Makefile.

Comprendre le code de Django en comparaison avec celui de Flask

Django fonctionne fondamentalement de la même manière que Flask pour le routage, mais il sépare les vues et le contrôleur :

  • on utilise généralement le fichier urls.py pour décrire la liste des urls proposées par une applications :
    • ces URL ne doivent pas se chevaucher, autrement une seule des URL serait prise en compte (la première rencontrée par Django) ;
    • la liste des URL de tous les fichiers urls.py de toutes les applications constitue l'API backend offerte par votre projet Django ;
  • on utilise le fichier views.py pour le code métier qui va recevoir les requêtes GET/POST HTTP ;
  • on peut éventuellement créer d'autres modules Python pour du code spécifique ;
  • on utilise le fichier models.py pour décrire les modèles des données de l'application, tels que ces données seront sauvegardées en base de données.

Le développement avec Django

Maintenant que vous avez vu l'installation de Django en virtualenv Python, ainsi que la théorie sur la manière dont il faut organiser le code Python, allons voir quelques exemples simples.

Hello World!

Obtenez la version _v3_application du dépôt. Elle contient un simple "Hello World!" si le client demande la page http://localhost:8000/, et une simple indication "Not found" pour toutes les autres pages. Comme pour chanque étape de ce tutoriel, la commande :

make

permet de faire le café (enfin presque). Par rapport à l'étape précédente, on dispose désormais d'une application Django dont le code Python est importé.

café au lait

Le café au lait que mon ordinateur ne sait toujours pas faire... Ça viendra.

En lisant le code,on s'aperçoit que son organisation est similaire à celle de Flask : d'un côté on route les urls, on utilise un ou plusieurs fichiers urls.py pour ce faire, et dans les vues du fichier views.py de chaque application du projet, on peut écrire le code à exécuter pour chaque requête.

urlpatterns = [
    url('^$', views.index),
    url(r'^admin/', admin.site.urls),
    url('^.*', appviews.p404),
]

Le routage des URL du projet (et de chacune des applications) est défini à l'aide de regex Python. Je fait correspondre les URL suivantes :

  • http://localhost:8000/
  • http://localhost:8000

au code ci-dessous :

def index(request):
    return HttpResponse("Hello World!")

à l'aide de la première règle de routage.

La page suivante de la documentation permet une utilisation avancée des URL de routage de l'application. On peut par exemple utiliser un champ de l'URL comme paramètre.

Je ne vous ai pas promis la lune mais presque, allons plus loin dans les fonctionnalités qui sont un des grands avantages de Django.

Mon premier gabarit

La version _v4_template permet d'utiliser les templates de Django. Je n'en fait qu'un usage très limité, je me limite à indiquer à Django où il doit aller chercher les fichiers de template correspondant au code HTML/CSS/JavaScript, servis par le serveur de développement, mais ce moteur de gabarits possède de nombreuses fonctionnalités, comme les variables, les tags, des boucles, des conditions...

La page accessible à l'URL par défaut http://localhost:8000/ affiche le code du template index.html :

def index(request):
    return render(request, "index.html", {})

J'ai rajouté de manière explicite un paramètre optionnel : le dictionnaire vide {}. Il sert à préciser des variables Python à transmettre au moteur de gabarits.

Toutes les autres requêtes redirigent vers la page 404.

Un accès à la base de données

Une grande quantité d'applications web nécessite une base de données. Django propose un moyen simple pour s'interfacer avec une base de données sans nécessairement en connaître les subtilités, fournit des garanties qui permettent éventuellement de changer de moteur de base de données, et permet au développeur de manipuler les objets en base facilement.

La version _v5_bdd présente un exemple simple d'utilisation d'une base de données. Les URL suivantes sont valides :

  • http://localhost:8000/create/ : crée un objet en base de données ;
  • http://localhost:8000/delete/ : supprime tous les objets ;
  • http://localhost:8000/get_nb_objects/ : affiche le nombre d'objets en base de données.

Attention, celles-ci ne le sont pas, il manque un / à la fin :

  • http://localhost:8000/create ;
  • http://localhost:8000/delete ;
  • http://localhost:8000/get_nb_objects.

Si vous vous demandez pourquoi l'exemple marche bien que tous les objets aient le même nom et la même valeur de booléen, c'est parce qu'ils se voient attribuer un identifiant entier automatique croissant si on ne définit pas d'attribut ayant une clef primaire, cf. la documentation.

Découpage en modules

Vous remarquerez l'utilisation de la primitive Django include dans le code, par rapport à la version _v4_template, qui permet de découper le routage vers les URL d'une application.

Utilisation d'un constructeur personnalisé

Django utilise la fonction __init__ qui sert à construire des objets Python. La documentation explique comment attacher un constructeur aux objets sérialisés en base de données, plutôt que de recourir à une méthode build comme je m'y suis pris dans l'exemple _v5_bdd. L'exemple se trouve dans le code de la version  _v6_constructeur.

Pour aller plus loin

Je ferai peut-être une suite à ce tutoriel Django. Dans tous les cas, sachez que le point auquel nous sommes arrivés n'est pas suffisant ! En effet, on utilise toujours le serveur de développement de Django, qui n'est pas fait pour être utilisé en production, entre autres :

"(notre métier est le développement d’environnements Web, pas de serveurs Web)."

Mise en production

D'autres éléments sont à considérer durant et après la phase de développement :

  • la mise en place d'un mécanisme de journalisation de l'application ;
  • la mise en place d'un chien de garde permettant de s'assurer que le serveur (nginx, apache, ou autre) sert toujours l'application que vous avez écrite ;
  • la mise en place d'une sauvegarde des données en base...

Il s'agit d'un travail d'administration système indispensable. À ce travail s'ajoute éventuellement celui de développer une interface web ou autres, si besoin est.

Modules

Je n'ai pas parlé des modules de Django. Dans la version _v4_bdd, on utilise la fonction Django render, qui permet de retourner une chaîne de caractères en temps que réponse HTTP. Cela diffère de Flask, qui permet de simplement retourner une chaîne de caractères. L'explication tient au fait que Django utilise l'objet request pour le passer aux modules Django, dans la fonction render.

Si vous êtes intéressés par le sujet, je vous conseille cette page, qui décrit les interactions possibles sur une requête, ainsi que la base de leur fonctionnement.

Réutilisation

Voilà un bon début de projet Django. Pour le réutiliser, n'oubliez pas :

  • de changer la clef API située dans le fichier mon_django/settings.py ;
  • de respecter la licence GPLv3.

Vous pouvez également créer un répertoire git de zéro à partir d'un des tags du dépôt exemple. Pour cela, obtenez les sources du dépôt au tag que vous souhaitez :

git checkout <tagname>

Puis supprimez le dépôt git sans toucher au fichiers existant en enlevant l'arborescence sous le répertoire .git :

rm -rf ./.git

Enfin, créez un nouveau dépôt :

git init

Vous pouvez aussi garder le dépôt git en l'état, le script del_tags.sh permet de supprimer les tags commençant par "_".

Conclusion

Mon but ici n'est pas de prétendre que Django est meilleur que Flask, comme je l'ai indiqué au début de ce tutoriel, j'ai très peu touché à Flask. Flask est un prétexte pour moi pour montrer un des différents aspects de Django (le routage des requêtes). Par ailleurs, en lisant un peu sur le sujet, on s'aperçoit que beaucoup de modules peuvent être utilisés pour donner à Flask des capacités similaires à celles qui viennent avec Django, par exemple :

  • une interface d'administration ;
  • un module ORM pour s'interfacer avec une base de données...

Il y a donc des arguments en faveur de Flask par rapport à Django, par exemple :

  • le code de Flask est composé de moins de lignes de code  ;
  • on peut choisir ce dont on a réellement besoin parmi des fonctionnalités offertes par la bibliothèque qu'on utilise...

Django, a de son côté :

  • l'organisation générale d'un projet web :
    • Django encourage à structurer son code ;
    • Django suggère la division du code en applications ;
    • il permet aux développeurs de travailler sur des applications différentes, ce qui facilite la collaboration ;
  • il vient avec un moteur de templates ;
  • il vient avec un ORM...

Cette comparaison n'est évidemment pas exhaustive, mais permet de se rendre compte des convergences et divergences de philosophie de ses deux projets.

Vous voilà parés pour démarrer ! J'espère que ce tutoriel vous a été utile. Pour ma part, je trouve que Django est très sympathique à utiliser, sa documentation est facile à prendre en main pour un usage débutant, même si elle est touffue. Enfin sachez que raspbeguy et moi-même avons quelques projets web (parmi lesquels clickstart, et Hashtagueule Express) qui utiliseront peut-être cette technologie.

Bonne journée et bon développement Python/Django !

Motius

PS : après avoir commencé cet article, je suis tombé sur celui-ci, en anglais. Je tiens à le mentionner car la majorité des articles Flask vs Django sont théoriques (souvent des descriptions fonctionnelles, assez intéressantes pour se familiariser rapidement avec les deux projets), mais je n'avais pas vu (ni vraiment cherché) d'article les comparant en mettant la main dans le code.