Préparer un bon projet Django

Préhistoire

Il fut un temps où déployer un projet web ne consistait qu'à uploader son code sur un FTP puis à configurer la base de données pour faire fonctionner son compteur de visites à gros digits verts. Maintenant nous avons des forges sociales, les frameworks web/css/js, les outils de déploiement et, pour les plus sérieux d'entre vous, des tests unitaires et fonctionnels.

Les pionniers pensaient qu'un simple django-manage startproject suffirait à leur éviter une bête série de copier-coller de boilerplates et de code cargo cult à seule fin de réinstaller un environnement confortable et compatible avec tous les outils d'intégration utilisés dans son projet.

Parfois même, les moins téméraires réglaient cette question par un clone de leur projet Django précédent avec série de sed et rename puis en retirant les modules spécifiques pour espérer ne pas oublier le petit module caché loin dans l'arborescence et qui est pourtant vachement pratique.

Faire la cuisine

Alors, pour les gens qui ont envie d'un petit en-cas rapide mais qui ont pas envie de devoir exécuter toute la recette, il existe les petits sachets auquel on rajoute un peu d'eau et un oeuf et qui régalent la famille sans que tata Christine nous accuse d'avoir eu la main lourde sur la fleur d'oranger.

Dans notre cas, le template Cookiecutter est notre petit sachet de code prêt à l'emploi. C'est un outil en ligne de commande permettant de générer rapidement et simplement des squelettes de projets à partir d'un template afin d'automatiser la mise en place d'un nouveau projet et ainsi gagner du temps. Une configuration peut être définie à la génération via une série de questions lors de l'exécution. On définit ces questions via un simple fichier json contenant des données par défaut, exemple:

{
    "project_name": "project_name",
    "author_name": "Your Name",
    "email": "Your email",
    "description": "A short description of the project.",
    "year": "Current year",
    "version": "0.1.0"
}

Ce n'est pas le premier outil à réaliser ce genre de tâche. Par exemple PasteScript permettait déjà de générer des templates de projet et mr.bob est une alternative pour les allergiques au gluten.

En cuisine comme en développement, chacun a son mot à dire quand il s'agit d'écrire une recette. Dans notre cas nous avons choisi les ingrédients suivants:

  • Django pour le framework web avec:
    • South pour la gestion des migrations de bases de données
    • Autoslug pour générer des slugs a partir d'un champ texte
    • Django-registration pour la gestion des utilisateurs (avec des templates !!)
  • Twitter Bootstrap pour le framework CSS
  • AngularJS en framework JS
  • Jquery en supplément (la petite pincée de sel)

Donc, en pratique, si vous avez envie d'initialiser un joli projet qui utilise toute la belle technologie du moment il vous suffira de taper les commandes suivantes:

    $ cookiecutter https://github.com/bearstech/cookiecutter-django-bootstrap-angular
    Cloning into 'cookiecutter-django-bootstrap-angular'...
    ...
    project_name (default is "project_name")? super_projet
    author_name (default is "Your Name")? Moi
    email (default is "Your email")? moi@example.com
    description (default is "A short description of the project.")?     Mon super projet
    year (default is "Current year")? 
    version (default is "0.1.0")? 
    ...
    $ cd super_projet
    $ bin/django-manage syncdb --migrate
    $ bin/django-serve

Ceci vous mijotera un magnifique squelette de projet:

super_projet
├── bin
├── bower.json
├── CHANGES.rst
├── CONTRIBUTORS.txt
├── docs
├── gulpfile.js
├── LICENSE.rst
├── MANIFEST.in
├── medias
├── package.json
├── README.rst
├── setup.cfg
├── setup.py
├── super_project
│   ├── __init__.py
│   ├── settings.py
│   ├── static
│   │   ├── bower_components
│   │   ├── css
│   │   └── js
│   ├── urls.py
│   ├── wsgi.py
├── templates
│   ├── 404.html
│   ├── 500.html
│   ├── base.html
│   └── home.html
├── tests
│   ├── __init__.py
│   ├── test_home.py
├── tox.ini

Comment cela fonctionne ?

Une fois que cookiecutter vous aura posé les questions permettant de personnaliser certaines valeurs de votre projet, il installera un squelette de base de votre projet. Il aura en fait remplacé les noms de fichiers, répertoires et variables au coeur de la recette par les valeurs que vous lui aurez donné lors de la première étape.

Ensuite, il lui sera possible de lancer un script à la génération pour exécuter buildout afin de bootstrapper vos outils: dépendances python, node.js, puis javascript avec bower ainsi que tout ce qui nous semble nécessaire à la mise en place d'un nouveau projet. Il n'y a ensuite plus qu'à commencer à coder !

Et si je veux modifier ma recette?

Le langage de templating utilisé par cookiecutter est Jinja2, on peut donc écrire des fichiers pré-remplis contenant quelques termes qu'on aura renseignés lors du questionnaire à la génération. Exemple pour le fichier settings de django:

ROOT_URLCONF = '{{ cookiecutter.project_name }}.urls'

Il est également possible d'écrire les noms des répertoires/fichiers en Jinja2. Exemple:

{{cookiecutter.project_name}}
├── templates
├── static
└── css
    └── {{cookiecutter.project_name}}.less

Et comme toute mamie qui aime garder ses recettes dans un carnet pour ensuite le recopier à qui le demanderait, vous pouvez stocker cette recette dans un dépôt Git et utiliser une fonctionnalité vraiment pratique de cookiecutter, qui est de pouvoir générer un squelette directement depuis ce dépôt (sur votre compte github par exemple) en une ligne de commande:

$ cookiecutter https://github.com//cookiecutter-django-bootstrap-angular

Mais qui la testera?

Une fois ceci fait, vous aurez à disposition un projet tout beau tout neuf, prêt à accueillir votre code métier.

Il vous est possible de lancer les tests unitaire python/django grâce à tox: un buildeur/runner qui permet de construire un environnement sain pour ensuite y lancer les tests. Il fonctionne aussi bien en local qu'avec jenkins ou travis-ci.

Le projet contient un fichier tox.ini et un simple test fonctionnel qui assure que la page d'accueil se rend correctement sans erreur. Ce test utilise django-webtest qui fournit une interface de test fonctionnel plus conviviale et intuitive que celle fournie par django.

Le projet contient aussi tout un tas de fichiers de configuration «standard» comme des fichiers de configuration pour coverage, flake8 et travis-ci qui vous aideront à assurer une relative qualité du code python contenu dans votre projet.

Gulp est un gestionnaire de tâches. Il permet d'automatiser un certain nombre de processus répétitifs, comme la compilation de fichier lesscss ou la concaténation de fichiers javascript. Nous le préférons à Grunt car il est plus minimaliste et tout aussi efficace.

Dans notre projet, Gulp compilera automatiquement les fichiers less en css avec un watcher défini dans le fichier gulpfile.js:

// Watch Files For Changes
gulp.task('watch', function() {
    gulp.watch('project_name/static/css/*.less', ['less']);
    gulp.watch(testFiles, ['test']);
});

En lancant gulp watch, à chaque modification de fichier .less, une compilation est effectuée. Il fonctionne de la même manière pour les tests unitaires angularjs. Les tests seront relancés à chaque modification d'un fichier javascript.

Si vous n'aimez pas avoir un watcher, il est possible de lancer les tests angularjs et compilations less de manière spontanée en invoquant les tâches spécifiques définies dans le fichier gulpfile.js

gulp test lancera les tests unitaires tandis que gulp less compilera les fichiers less en css.

C'est intéressant mais ça serait meilleur avec...

On vous voit venir. C'est juste une recette de projet comme on aime en faire chez Bearstech. Et comme nous sommes des enthousiastes du libre nous partageons notre petit livre de recettes.

Si vous souhaitez en modifier les ingrédients, nous proposer des variations, voire faire quelque chose de tout à fait original n'hésitez pas à forker notre projet!. Nous serons ravis de savoir que ce billet aura pu vous faire découvrir ce petit outil et ainsi vous faciliter, à vous aussi, la vie.

Article co-écrit par Gael Pasgrimaud, Maxime Moraine et Johan Charpentier