Stack front-end moderne avec Django

Ce billet date de plusieurs années, ses informations peuvent être devenues obsolètes.

Le temps de la (toute relative) simplicité d’un front-end constitué d’un fichier CSS et d’un fichier JavaScript semble révolu. Je m’en suis souvent plaint dans mes conversations d’old-timer ;)

En effet les techniques et les outils front-end se sont multipliés pendant ces dernières années, avec pour corollaire une complexité accrue et l’apparition du besoin d’automatiser les tâches.

Je vais vous expliquer comment je gère ces problèmes en ce moment en détaillant la stack front-end mise en place sur MarcArea et son intégration avec Django.

Elle est inspirée de mes récentes lectures, de mes expériences sur différents autres projets et de mes échanges sur le sujet avec d’autres dévelopeurs qui ont toute ma confiance.

La plupart des outils utilisés correspondent quasiment aux résultats de cette étude réalisée pendant l’été 2014 dont l’échantillon est faible mais extrêmement représentatif.

Le système de build

Comme tous les (NewCool Kids (on the Block) j’utilise Grunt pour automatiser le build et je n’ai pas encore eu un seul emmerdement avec (bruitage d’applaudissements enregistrés). Il est facile à utiliser et bien documenté même sans pousser le délire trop loin (hint: load-grunt-tasks est top).

Il existe des tonnes d’alternatives dont le bon vieux make mais rester sur le même langage (JavaScript) pour le code du build me semble une bonne option, et puis :

Makefile syntax is ugly and deprecated.

Lol. Désolé. J’ai pourtant essayé de conserver le trolomètre en dessous de 9000. Je n’ajoute pas de lien vers la source de cette citation car j’ai trop honte.

Une autre alternative qui a bien buzzé est Gulp, mais en l’utilisant vous riquez la bascule du côté hipster de la force :)

La gestion des dépendances

Je me suis dit qu’en cas d’indisponibilité de PyPI j’avais déjà un SPOF dans mon système de build, alors un de plus, un de moins… :D

J’utilise npm (via ses devDependencies) pour toutes les dépendances du système de build en lui-même, et Bower pour les dépendances client, c’est à dire le code de tierce partie exécuté dans le navigateur. Je vous conseille la lecture de cet article pour en apprendre davantage.

Les paquets de ces gestionnaires utilisent le semantic versioning. Remarque tout à fait personnelle de votre serviteur : ça ne veut pas dire que tous les paquets sont bien packagés :)

N’oubliez pas l’existence de prune à inclure dans votre système de build pour nettoyer les paquets inutilisés.

Je ne conserve pas la source des dépendances dans mon dépôt de code. C’est un choix qui est sujet à débat mais vous avez déjà vu quelqu’un versionner ses dépendances PyPI ? Non ! On a pas vu ça depuis 2010 ;)

L’intégration avec Django

J’ai vu différentes configurations des static files au fil du temps. Je me suis souvent retrouvé avec des statics éclatés un peu partout (souvent dupliqués) et un dossier à part bien fat pour stocker les parties communes.

Ça ne pouvait plus durer, vous comprenez ? Il faut garder la maison propre ;)

Une solution qui marche étonnamment bien est de sortir complètement les statics du répertoire contenant le code Django, ce qui donne une arborescence semblable à :

project_root/
    ├── manage.py
    ├── project_django/
    │   ├── .pylintrc
    │   ├── app_1/
    │   ├── app_2/
    │   ├── requirements.txt
    │   └── settings/
    └── project_ui/
        ├── .jshintrc
        ├── bower.json
        ├── bower_components/   (.gitignore)
        ├── dist/               (.gitignore)
        ├── Gruntfile.js
        ├── node_modules/       (.gitignore)
        ├── package.json
        ├── src/
        └── test/

À l’intérieur du répertoire contenant les statics, le répertoire dist n’est pas versionné et contient les fichiers front-end générés. Il est ajouté dans les settings de STATICFILES_DIRS afin d’être découvert par l’application des staticfiles. L’organisation des fichiers au sein de ce répertoire est établie par les tâches Grunt, et je garde toujours en tête le concept du Static file namespacing que je trouve un chouïa sous-développé dans la documentation Django.

Le répertoire test parle de lui même. Il est juste et de bon ton d’appliquer au code front-end la même hygiène qu’au code back-end.

Le répertoire src est le nerf de la guerre. Je me suis beaucoup inspiré du chapitre Organizing files and folders (AngularJS centric mais très intéressant) pour établir ma structure mais il y a plein d’autres bonnes idées. Atteint de flemmingite aiguë, je ne vais pas tout détailler mais plutôt vous coller un screenshot à partir duquel vous pourrez inférer les objectifs et avantages de cette organisation.

Activation du mode maxi flemme :

La syntaxe de configuration des tâches Grunt permet d’avoir une excellente flexibilité de l’organisation des fichiers, a.k.a. si demain j’ai une meilleure idée de la structure de src je change live & direct.

La touche finale : ajoutez un petit gruntserver pour agrémenter le tout et là, normalement, vous êtes pas mal.

Éviter la mise en cache des statics

I’m ghost proof! Bustin’ those fuckers!!!

La source de cette citation est malencontreusement inconnue mais remplacez-y ghost par cache et on entre dans le vif du sujet.

Mon objectif ici était d’ajouter le hash MD5 des fichiers CSS et JS à leur nom. J’ai commencé par essayer de trouver une solution avec Grunt avant de me rendre compte que Django peut déjà le faire tout seul comme un grand via ManifestStaticFilesStorage ou CachedStaticFilesStorage.

Pour ce faire :

  • il faut paramétrer le setting STATICFILES_STORAGE avec ManifestStaticFilesStorage ou CachedStaticFilesStorage
  • s’assurer que le setting DEBUG est défini à False
  • utiliser le bon template tag ({% load static from staticfiles %}) pour référencer les statics dans les templates Django (un {% load static %} seul ne fonctionne pas !) : <script src="{% static "js/marcarea.js" %}"></script>
  • s’assurer que collectstatic a bien fait son travail

Et là, c’est magique, ça marche tout seul :)

#1 Thibault

15/10/2014 11:08

Troller la syntaxe de Make pour utiliser Grunt à la place, c'est un peu chercher les coups, non ? :)

Sinon, j'ai testé un workflow pur js similaire au tiens, mais j'ai galéré avec l'intégration de multiples technos. Pour la gestion des assets, j'utilise maintenant django-pipeline, et django-casper pour les tests d'applis un peu lourdes niveau js.

#2 Kemar

15/10/2014 11:30

@Thibault le troll sur la syntaxe de Make c'est juste pour le fun ;)

Avant TextMate 2 Après Migrer un projet Django de MySQL à PostgreSQL

Tag Kemar Joint