Good practices when starting a Django application [fr]

Voilà un petit guide de quelques bonnes pratiques utiles au lancement d’un projet Django.

Architecture de base

Créer un dépôt Git, contenant les fichiers suivants :

  • AUTHORS
  • LICENSE
  • CHANGELOG
  • README.rst
  • .gitignore

Ajouter un dossier ayant le nom de l’application, et qui la contiendra.

Pour le .gitignore, un bon début pourrait être :

*~
*.orig
*.swp
*.pyc
.DS_Store
docs/_build/
.coverage

Pour le README.rst, l’essentiel est d’inclure une rapide description de votre application ainsi qu’un exemple d’utilisation (show me the code !). Je vous conseille ce manuel, ou ce petit mémo, pour la syntaxe du reStructuredText.

Tests

Pour lancer les tests sur votre unique application sans avoir besoin de créer de projet complet, vous pouvez utiliser ce petit script. Si vous avez besoin de définir des modèles spécifiquement pour vos tests, vous pouvez les mettre au début du fichier tests.py.

Travis-CI

Travis-CI est un outil qui va vous permettre d’automatiser vos tests : à chaque commit effectué, tous vos tests seront lancés, et Travis vous préviendra si certains tests ne passent plus.

L’autre avantage de Travis-CI est qu’il est possible d’effectuer vos tests avec les versions de Python/Django de votre choix.

Pour cela, il vous faut créer un fichier .travis.yml dans votre dépôt. Voir la doc pour plus d’infos sur ce fichier.

Je vous propose aussi mon .travis.yml, en guise d’exemple :

language: python

python:
- 2.6
- 2.7
- 3.2
- 3.3

env:
- DJANGO_VERSION=1.3
- DJANGO_VERSION=1.4
- DJANGO_VERSION=1.5

install:
# This is a dependency of our Django test script
- pip install argparse --use-mirrors

# Install the dependencies of the app itself
# - pip install -r requirements.txt --use-mirrors

- pip install -q Django==$DJANGO_VERSION --use-mirrors

- pip install coverage

- pip install pep8 --use-mirrors
- pip install pyflakes --use-mirrors

before_script:
    - "pep8 --ignore=E501 {{APP_NAME}}"
    - pyflakes {{APP_NAME}}

script:
    - coverage run quicktest.py {{APP_NAME}}

after_success:
- pip install coveralls
- coveralls


# We need to exclude old versions of Django for tests with python 3.
matrix:
    exclude:
        - python: 3.2
        env: DJANGO_VERSION=1.3
        - python: 3.3
        env: DJANGO_VERSION=1.3
        - python: 3.2
        env: DJANGO_VERSION=1.4
        - python: 3.3
        env: DJANGO_VERSION=1.4

Il a l’avantage de lancer les tests à la fois avec python 2, mais aussi avec python 3 ! Pour cela, il suffit d’indiquer à Travis de ne pas tester les versions de Django qui ne sont pas compatibles avec python 3 (en les excluant de la « build matrix »).

Ce .travis.yml va également utiliser pep8 et pyflakes pour s’assurer que votre code respecte bien les bonnes pratiques.

Couverture de code

Il existe des outils qui vous permettent d’analyser quelles sont les parties de votre code qui sont exécutées pendant les tests, et lesquelles ne le sont pas.

En python, il existe un outil nommé coverage qui fait exactement cela.

Pour l’utiliser, il suffit d’exécuter la commande :

coverage run votre_script.py

Un fichier .coverage sera alors créé, et vous pourrez avoir un rapport sur la couverture de votre code en exécutant coverage report. Il est même possible, avec coverage html, d’obtenir un rapport détaillé contenant votre code source annoté.

Coveralls

Coveralls est un petit service qui se greffe à Travis. Il permet d’obtenir automatiquement à chaque commit, un compte rendu avec le pourcentage des lignes de votre projet qui sont couvertes par vos tests.

Je vous conseille de regarder mon .travis.yml, qui contient toutes les commandes nécessaires pour utiliser coveralls.

Il est possible que coverage regarde aussi le code que vous couvrez à l’intérieur même de Django, vous donnant des nombres assez démesurés et peu représentatifs. Auquel cas, il suffit d’ajouter un fichier .coveragerc au projet, contenant :

[run]
source = dossier_de_l'application

Documentation

Pour documenter le projet, nous allons utiliser Sphinx, qui permet d’écrire la documentation en ReST, et est même capable d’aller chercher celle contenue dans les docstrings pour l’inclure dans la doc.

Il suffit de créer un répertoire « docs » dans votre projet, et de lancer la commande sphinx-quickstart qui vous guidera pour la création de votre projet.

Une fois votre documentation générée, vous pouvez utiliser Read the Docs, service capable de construire et d’héberger gratuitement de la documentation.

PyPI

Après publication de votre projet, il est bon de le publier sur PyPI, qui référence tous les paquets python et permet d’automatiser l’installation de votre paquet,

Pour cela, il suffit de créer un fichier setup.py, contenant par exemple :

from distutils.core import setup

with open('README.rst', 'r') as f:
    long_description = f.read()

with open('VERSION', 'r') as f:
    version = f.read().strip()

setup(
    name = 'django-safedelete',
    packages = ['django-safedelete'],
    version = version,
    description = 'Mask your objects instead of deleting them from your database.',
    long_description = long_description,
    author = 'Korantin Auguste',
    author_email = 'contact@palkeo.com',
    url = 'https://github.com/makinacorpus/django-safedelete',
    download_url = 'https://github.com/makinacorpus/django-safedelete/tarball/%s' % version,
    keywords = ['django', 'delete', 'safedelete', 'softdelete'],
    classifiers = [
        "Framework :: Django",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: BSD License",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 2.7",
        "Development Status :: 4 - Beta",
    ],
    license='BSD',
    requires=['Django (>= 1.3)']
)

Avant d’envoyer votre projet sur PyPI, le plus simple est de créer un « tag » sur votre dépôt git :

git tag 0.1

En spécifiant le numéro de version souhaitée.

Github va ensuite automatiquement publier une archive du dépôt, qu’il vous suffira d’envoyer sur PyPI avec la commande python setup.py register.

Allez voir ici pour plus d’infos. Ce très bon article pourra aussi vous être utile.

zest.releaser

Pour publier une nouvelle version de votre projet, il faudrait incrémenter votre numéro de version à la main, puis mettre à jour votre changelog, puis faire un tag sur git, puis uploader votre paquet sur PyPI…

Afin d’éviter de faire cela à la main, vous pouvez utiliser zest.releaser : il vous fournira des commandes (en particulier la commande fullrelease), qui vont se charger de faire tout ça pour vous.

TL;DR

Parce qu’il est pénible de faire tout ceci à la création de chaque application, j’ai créé un « template » d’application : django-app-template. Le répertoire « template » de ce dépôt contient donc ce modèle. Mais il y a mieux : Django vous permet de créer une application en vous basant sur un dossier de template.

La commande suivante va donc créer pour vous un répertoire contenant votre nouvelle application, basée sur un modèle :

django-admin.py startapp --template=django-app-template/template --extension=py,rst <app_name>

Le meilleur dans tout ça, c’est que ce système utilise le langage de template de Django, pour donner vos fichiers de base à partir du « template » d’application.

À partir de là, je me suis même dit qu’il pourrait être possible de rajouter des variables supplémentaires dans ces template, avec pourquoi pas le nom de l’auteur, etc… Ce qui donne ce ticket :)