Version en ligne

Tutoriel : Développez votre site web avec le framework Django

Table des matières

Développez votre site web avec le framework Django
Créez vos applications web avec Django
Qu'est-ce qu'un framework ?
Qu'est-ce que Django ?
Téléchargement et installation
Le fonctionnement de Django
Un peu de théorie : l'architecture MVC
La spécificité de Django : le modèle MVT
Projets et applications
Gestion d'un projet
Créons notre premier projet
Configurez votre projet
Créons notre première application
Les bases de données et Django
Une base de données, c'est quoi ?
Le langage SQL et les gestionnaires de base de données
La magie des ORM
Le principe des clés étrangères
Votre première page grâce aux vues
Hello World !
Routage d'URL : comment j'accède à ma vue ?
Organiser proprement vos URL
Passer des arguments à vos vues
Des réponses spéciales
Les templates
Lier template et vue
Affichons nos variables à l'utilisateur
Manipulons nos données avec les tags
Ajoutons des fichiers statiques
Les modèles
Créer un modèle
Jouons avec des données
Les liaisons entre modèles
Les modèles dans les vues
L'administration
Mise en place de l'administration
Première prise en main
Administrons nos propres modèles
Personnalisons l'administration
Les formulaires
Créer un formulaire
Utiliser un formulaire dans une vue
Créons nos propres règles de validation
Des formulaires à partir de modèles
La gestion des fichiers
Enregistrer une image
Afficher une image
Encore plus loin
TP : un raccourcisseur d'URL
Cahier des charges
Correction
Les vues génériques
Premiers pas avec des pages statiques
Lister et afficher des données
Agir sur les données
Techniques avancées dans les modèles
Les requêtes complexes avec Q
L'agrégation
L'héritage de modèles
L'application ContentType
Simplifions nos templates : filtres, tags et contextes
Préparation du terrain : architecture des filtres et tags
Personnaliser l'affichage de données avec nos propres filtres
Les contextes de templates
Des structures plus complexes : les custom tags
Les signaux et middlewares
Notifiez avec les signaux
Contrôlez tout avec les middlewares
Les utilisateurs
Commençons par la base
Passons aux vues
Les vues génériques
Les permissions et les groupes
Les messages
Les bases
Dans les détails
La mise en cache
Cachez-vous !
Quand les données jouent à cache-cache
La pagination
Exerçons-nous en console
Utilisation concrète dans une vue
L'internationalisation
Qu'est-ce que le i18n et comment s'en servir ?
Traduire les chaînes dans nos vues et modèles
Traduire les chaînes dans nos templates
Sortez vos dictionnaires, place à la traduction !
Le changement de langue
Les tests unitaires
Nos premiers tests
Testons des vues
Ouverture vers de nouveaux horizons : django.contrib
Vers l'infini et au-delà
Dynamisons nos pages statiques avec flatpages !
Rendons nos données plus lisibles avec humanize
Déployer votre application en production
Le déploiement
Gardez un œil sur le projet
Hébergeurs supportant Django
L'utilitaire manage.py
Les commandes de base
La gestion de la base de données
Les commandes d'applications

Développez votre site web avec le framework Django

Django
« Le framework web pour les perfectionnistes sous pression »

Image utilisateurEn quelques années, les sites web n'ont cessé d'évoluer. Ils requièrent désormais des développements longs et acharnés, sans oublier le fait que ceux-ci peuvent parfois devenir très complexes et se mesurer en milliers de lignes de code.
Aujourd'hui, la simple page web ne suffit plus, et que ce soit dans un cadre professionnel ou personnel, les attentes sont de plus en plus lourdes.

C'est de ce constat qu'est né Django : proposer un développement plus efficace et plus rapide d'une application dynamique web, tout en conservant la qualité ! Ce cours vous apprendra à construire des sites web complexes et élégants, et en un temps record.

Ce cours porte sur la version 1.5 de Django, et n'assure nullement que toutes les méthodes présentées fonctionneront forcément sur des versions antérieures ou postérieures.

Créez vos applications web avec Django

Qu'est-ce qu'un framework ?

Si vous lisez ceci, c'est que vous avez décidé de vous lancer dans l'apprentissage de Django. Avant de commencer, des présentations s'imposent : Django est un framework web écrit en Python, qui se veut complet tout en facilitant la création d'applications web riches.

Avant de commencer à écrire du code, nous allons tout d'abord voir dans ce chapitre ce qu'est un framework en général, et plus particulièrement ce qu'est Django. Dans un second temps, nous verrons comment l'installer sur votre machine, pour pouvoir commencer à travailler ! Est-il utile de vous rappeler encore ici qu'il est nécessaire d'avoir les bases en Python pour pouvoir commencer ce cours ?

Qu'est-ce qu'un framework ?

Créez vos applications web avec Django Qu'est-ce que Django ?

Qu'est-ce qu'un framework ?

Un framework est un ensemble d'outils qui simplifie le travail d'un développeur. Traduit littéralement de l'anglais, un framework est un « cadre de travail ». Il apporte les bases communes à la majorité des programmes ou des sites web. Celles-ci étant souvent identiques (le fonctionnement d'un espace membres est commun à une très grande majorité de sites web de nos jours), un développeur peut les réutiliser simplement et se concentrer sur les particularités de son projet.

Il s'agit donc d'un ensemble de bibliothèques coordonnées, qui permettent à un développeur d'éviter de réécrire plusieurs fois une même fonctionnalité, et donc d'éviter de réinventer constamment la roue. Inutile de dire que le gain en énergie et en temps est considérable !

Quels sont les avantages d'un framework ?

Un framework instaure en quelque sorte sa « ligne de conduite ». Tous les développeurs Django codent de façon assez homogène (leurs codes ont le même fonctionnement, les mêmes principes). De ce fait, lorsqu'un développeur rejoint un projet utilisant un framework qu'il connaît déjà, il comprendra très vite ce projet et pourra se mettre rapidement au travail.

Le fait que chaque framework possède une structure commune pour tous ses projets a une conséquence tout aussi intéressante : en utilisant un framework, votre code sera le plus souvent déjà organisé, propre et facilement réutilisable par autrui.

Voici d'ailleurs un grand défi des frameworks : bien que ceux-ci doivent instaurer une structure commune, ils doivent aussi être souples et modulables, afin de pouvoir être utilisés pour une grande variété de projets, du plus banal au plus exotique. Autrement, leur intérêt serait grandement limité !

Quels sont les désavantages d'un framework ?

Honnêtement, il n'existe pas vraiment de désavantages à utiliser un framework. Il faut bien évidemment prendre du temps à apprendre à en manier un, mais ce temps d'apprentissage est largement récupéré par la suite, vu la vitesse de développement qui peut parfois être décuplée. Nous pourrions éventuellement dire que certains frameworks sont parfois un peu trop lourds, mais il incombe à son utilisateur de choisir le bon framework, adapté à ses besoins.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Créez vos applications web avec Django Qu'est-ce que Django ?

Qu'est-ce que Django ?

Qu'est-ce qu'un framework ? Téléchargement et installation

Qu'est-ce que Django ?

Django est donc un framework Python destiné au web. Ce n'est pas le seul dans sa catégorie, nous pouvons compter d'autres frameworks Python du même genre comme web2py, TurboGears, CherryPy ou Zope. Il a cependant le mérite d'être le plus exhaustif, d'automatiser un bon nombre de choses et de disposer d'une très grande communauté.

Le logo de Django
Le logo de Django

Django est né en 2003 dans une agence de presse qui devait développer des sites web complets dans des laps de temps très courts (d'où l'idée du framework). En 2005, l'agence de presse Lawrence Journal-World décide de publier Django au grand public, le jugeant assez mature pour être réutilisé n'importe où. Trois ans plus tard, la fondation Django Software est créée par les fondateurs du framework afin de pouvoir maintenir celui-ci et la communauté très active qui l'entoure.

Aujourd'hui, Django est devenu très populaire et est utilisé par des sociétés du monde entier, telles qu'Instagram, Pinterest, et même la NASA !

Logos d'Instagram, de la NASA et de Pinterest
Logos d'Instagram, de la NASA et de Pinterest
Pourquoi ce succès ?

Si Django est devenu très populaire, c'est notamment grâce à sa philosophie, qui a su séduire de nombreux développeurs et chefs de projets. En effet, le framework prône le principe du « Don't repeat yourself », c'est-à-dire en français « Ne vous répétez pas », et permet le développement rapide de meilleures et plus performantes applications web, tout en conservant un code élégant.

Django a pu appliquer sa philosophie de plusieurs manières. Par exemple, l'administration d'un site sera automatiquement générée, et celle-ci est très facilement adaptable. L'interaction avec une base de données se fait via un ensemble d'outils spécialisés et très pratiques. Il est donc inutile de perdre son temps à écrire directement des requêtes destinées à la base de données, car Django le fait automatiquement. De plus, d'autres bibliothèques complètes et bien pensées sont disponibles, comme un espace membres, ou une bibliothèque permettant la traduction de votre application web en plusieurs langues.

Une communauté à votre service

Évidemment, Django dispose des avantages de tous les frameworks en général. Il est soutenu par une communauté active et expérimentée, qui publie régulièrement de nouvelles versions du framework avec de nouvelles fonctionnalités, des corrections de bugs, etc.

Encore un point, et non des moindres, la communauté autour de Django a rédigé au fil des années une documentation très complète sur docs.djangoproject.com. Bien que celle-ci soit en anglais, elle reste très accessible pour des francophones. Nous ne pouvons que vous conseiller de la lire en parallèle de ce cours si vous voulez approfondir un certain sujet ou si certaines zones d'ombre persistent.

Enfin, pour gagner encore plus de temps, les utilisateurs de Django ont généralement l'esprit open source et fournissent une liste de snippets, des portions de code réutilisables par n'importe qui. Un site est dédié à ces snippets. Si vous devez vous attaquer à une grosse application ou à une portion de code particulièrement difficile, n'hésitez pas à aller chercher dans les snippets, vous y trouverez souvent votre bonheur !

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Qu'est-ce qu'un framework ? Téléchargement et installation

Téléchargement et installation

Qu'est-ce que Django ? Le fonctionnement de Django

Téléchargement et installation

Maintenant que nous avons vu les avantages qu'apporte Django, il est temps de passer à son installation. Tout d'abord, assurez-vous que vous disposez bien d'une version de Python supérieure ou égale à la 2.6.5 pour la branche 2.6.x ou à la 2.7.3 pour la branche 2.7.x et supérieure. Pour plus d'informations à ce sujet, vous pouvez vous reporter au cours sur le Python du Site du Zéro.

Il est également plus prudent de supprimer toutes les anciennes installations de Django, si vous en avez déjà. Il peut y avoir des conflits entre les versions, notamment lors de la gestion des projets. Il est essentiel de n'avoir que Django 1.5 sur votre machine, à part si vous avez déjà des applications en production sur des versions antérieures. Dans ce cas, il est conseillé soit de porter toutes vos applications pour Django 1.5, soit d'exécuter vos deux projets avec deux versions de Django bien indépendantes.

Linux et Mac OS

Sous Linux et Mac OS, l'installation de Django peut s'effectuer de deux manières différentes, soit en utilisant le gestionnaire de paquets de votre distribution (ou MacPorts pour Mac OS), soit en installant Django manuellement, via une archive officielle. Nous ne couvrirons pas la première solution, celle-ci dépendant beaucoup trop de votre distribution. Si toutefois vous choisissez cette solution, faites attention à la version de Django disponible dans les dépôts. Il se peut que ce ne soit pas toujours la dernière version qui soit disponible, donc pas à jour et incompatible avec ce cours.

Si vous ne passez pas par les dépôts, le plus simple reste de télécharger une archive. Il suffit ensuite de l'extraire et de l'installer, en effectuant les commandes suivantes dans une console :

tar xzvf Django-1.5.tar.gz
cd Django-1.5
sudo python setup.py install
Windows

Contrairement aux environnements UNIX, l'installation de Django sous Windows requiert quelques manipulations supplémentaires. Téléchargez l'archive de Django et extrayez-la. Avant de continuer, nous allons devoir modifier quelques variables d'environnement, afin de permettre l'installation du framework. Pour cela (sous Windows 7) :

  1. Rendez-vous dans les informations générales du système (via le raccourci Windows + Pause) ;

  2. Cliquez sur Paramètres système avancés, dans le menu de gauche ;

  3. Une fois la fenêtre ouverte, cliquez sur Variables d'environnement ;

  4. Cherchez la variable système (deuxième liste) Path et ajoutez ceci en fin de ligne (faites attention à votre version de Python) : ;C:\Python27\;C:\Python27\Lib\site-packages\django\bin\. Respectez bien le point-virgule permettant de séparer le répertoire de ceux déjà présents, comme indiqué à la figure suivante.

Édition du Path sous Windows 7
Édition du Path sous Windows 7

Validez, puis quittez. Nous pouvons désormais installer Django via la console Windows (Windows + R puis la commande cmd) :

cd C:\Users\<nom_d'utilisateur>\Downloads\django1.5  # A adapter à votre répertoire de téléchargement 
python setup.py install

Les fichiers sont ensuite copiés dans votre dossier d'installation Python (ici C:\Python27).

Vérification de l'installation

Dès que vous avez terminé l'installation de Django, lancez une nouvelle console Windows, puis lancez l'interpréteur Python (via la commande python) et tapez les deux lignes suivantes :

>>> import django
>>> print django.get_version()
1.5 # <- Résultat attendu

Si vous obtenez également 1.5 comme réponse, félicitations, vous avez correctement installé Django !

Dans la suite de ce cours, nous utiliserons SQLite, qui est simple et déjà inclus dans les bibliothèques de base de Python. Si vous souhaitez utiliser un autre système de gestion de base de données, n'oubliez pas d'installer les outils nécessaires (dépendances, packages, etc.).

En résumé
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Qu'est-ce que Django ? Le fonctionnement de Django

Le fonctionnement de Django

Téléchargement et installation Un peu de théorie : l'architecture MVC

Attaquons-nous au vif du sujet ! Dans ce chapitre, théorique mais fondamental, nous allons voir comment sont construits la plupart des frameworks grâce au modèle MVC, nous aborderons ensuite les spécificités du fonctionnement de Django et comment les éléments d'une application classique Django s'articulent autour du modèle MVT, que nous introduirons également. En dernier lieu, nous expliquerons le système de projets et d'applications, propre à Django, qui permet une séparation nette, propre et précise du code.

Au terme de ce chapitre, vous aurez une vue globale sur le fonctionnement de Django, ce qui vous sera grandement utile lorsque vous commencerez à créer vos premières applications.

Un peu de théorie : l'architecture MVC

Le fonctionnement de Django La spécificité de Django : le modèle MVT

Un peu de théorie : l'architecture MVC

Lorsque nous parlons de frameworks qui fournissent une interface graphique à l'utilisateur (soit une page web, comme ici avec Django, soit l'interface d'une application graphique classique, comme celle de votre traitement de texte par exemple), nous parlons souvent de l'architecture MVC. Il s'agit d'un modèle distinguant plusieurs rôles précis d'une application, qui doivent être accomplis. Comme son nom l'indique, l'architecture (ou « patron ») Modèle-Vue-Contrôleur est composé de trois entités distinctes, chacune ayant son propre rôle à remplir.

Tout d'abord, le modèle représente une information enregistrée quelque part, le plus souvent dans une base de données. Il permet d'accéder à l'information, de la modifier, d'en ajouter une nouvelle, de vérifier que celle-ci correspond bien aux critères (on parle d'intégrité de l'information), de la mettre à jour, etc. Il s'agit d'une interface supplémentaire entre votre code et la base de données, mais qui simplifie grandement les choses, comme nous le verrons par la suite.

Ensuite la vue qui est, comme son nom l'indique, la visualisation de l'information. C'est la seule chose que l'utilisateur peut voir. Non seulement elle sert à présenter une donnée, mais elle permet aussi de recueillir une éventuelle action de l'utilisateur (un clic sur un lien, ou la soumission d'un formulaire par exemple). Typiquement, un exemple de vue est une page web, ni plus, ni moins.

Finalement, le contrôleur prend en charge tous les événements de l'utilisateur (accès à une page, soumission d'un formulaire, etc.). Il se charge, en fonction de la requête de l'utilisateur, de récupérer les données voulues dans les modèles. Après un éventuel traitement sur ces données, il transmet ces données à la vue, afin qu'elle s'occupe de les afficher. Lors de l'appel d'une page, c'est le contrôleur qui est chargé en premier, afin de savoir ce qu'il est nécessaire d'afficher.

Schéma de l'architecture MVC
Schéma de l'architecture MVC
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Le fonctionnement de Django La spécificité de Django : le modèle MVT

La spécificité de Django : le modèle MVT

Un peu de théorie : l'architecture MVC Projets et applications

La spécificité de Django : le modèle MVT

L'architecture utilisée par Django diffère légèrement de l'architecture MVC classique. En effet, la « magie » de Django réside dans le fait qu'il gère lui-même la partie contrôleur (gestion des requêtes du client, des droits sur les actions…). Ainsi, nous parlons plutôt de framework utilisant l'architecture MVT : Modèle-Vue-Template.

Cette architecture reprend les définitions de modèle et de vue que nous avons vues, et en introduit une nouvelle : le template (voir figure suivante). Un template est un fichier HTML, aussi appelé en français « gabarit ». Il sera récupéré par la vue et envoyé au visiteur ; cependant, avant d'être envoyé, il sera analysé et exécuté par le framework, comme s'il s'agissait d'un fichier avec du code. Django fournit un moteur de templates très utile qui permet, dans le code HTML, d'afficher des variables, d'utiliser des structures conditionnelles (if/else) ou encore des boucles (for), etc.

Schéma d'exécution d'une requête
Schéma d'exécution d'une requête

Concrètement, lorsque l'internaute appelle une page de votre site réalisé avec Django, le framework se charge, via les règles de routage URL définies, d'exécuter la vue correspondante. Cette dernière récupère les données des modèles et génère un rendu HTML à partir du template et de ces données. Une fois la page générée, l'appel fait chemin arrière, et le serveur renvoie le résultat au navigateur de l'internaute.

On distingue les quatre parties qu'un développeur doit gérer :

On en revient donc au modèle MVT. Le développeur se doit de fournir le modèle, la vue et le template. Une fois cela fait, il suffit juste d'assigner la vue à une URL précise, et la page est accessible.

Si le template est un fichier HTML classique, un modèle en revanche sera écrit sous la forme d'une classe où chaque attribut de celle-ci correspondra à un champ dans la base de données. Django se chargera ensuite de créer la table correspondante dans la base de données, et de faire la liaison entre la base de données et les objets de votre classe. Non seulement il n'y a plus besoin d'écrire de requêtes pour interagir avec la base de données, mais en plus le framework propose la représentation de chaque entrée de la table sous forme d'une instance de la classe qui a été écrite. Il suffit donc d'accéder aux attributs de la classe pour accéder aux éléments dans la table et pouvoir les modifier, ce qui est très pratique !

Enfin, une vue est une simple fonction, qui prend comme paramètres des informations sur la requête (s'il s'agit d'une requête GET ou POST par exemple), et les paramètres qui ont été donnés dans l'URL. Par exemple, si l'identifiant ou le nom d'un article du blog a été donné dans l'URL crepes-bretonnes.com/blog/faire-de-bonnes-crepes, la vue récupérera faire-de-bonnes-crepes comme titre et cherchera dans la base de données l'article correspondant à afficher. Suite à quoi la vue générera le template avec le bon article et le renverra à l'utilisateur.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Un peu de théorie : l'architecture MVC Projets et applications

Projets et applications

La spécificité de Django : le modèle MVT Gestion d'un projet

Projets et applications

En plus de l'architecture MVT, Django introduit le développement d'un site sous forme de projet. Chaque site web conçu avec Django est considéré comme un projet, composé de plusieurs applications. Une application consiste en un dossier contenant plusieurs fichiers de code, chacun étant relatif à une tâche du modèle MVT que nous avons vu. En effet, chaque bloc du site web est isolé dans un dossier avec ses vues, ses modèles et ses schémas d'URL.

Lors de la conception de votre site, vous allez devoir penser aux applications que vous souhaitez développer. Voici quelques exemples d'applications :

Ce principe de séparation du projet en plusieurs applications possède deux avantages principaux :

Organisation d'un projet Django et réutilisation d'une application
Organisation d'un projet Django et réutilisation d'une application

Ici, le développement du système d'articles sera fait une fois uniquement. Pour le second site, une légère retouche des templates suffira. Ce système permet de voir le site web comme des boîtes que nous agençons ensemble, accélérant considérablement le développement pour les projets qui suivent.

En résumé
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

La spécificité de Django : le modèle MVT Gestion d'un projet

Gestion d'un projet

Projets et applications Créons notre premier projet

Django propose un outil en ligne de commandes très utile qui permet énormément de choses :

Nous verrons dans ce chapitre comment utiliser cet outil, la structure d'un projet Django classique, comment créer ses projets et applications, et leur configuration.

Créons notre premier projet

Gestion d'un projet Configurez votre projet

Créons notre premier projet

L'outil de gestion fourni avec Django se nomme django-admin.py et il n'est accessible qu'en ligne de commandes. Pour ce faire, munissez-vous d'une console MS-DOS sous Windows, ou d'un terminal sous Linux et Mac OS X.

Sous Windows, allez dans le menu Démarrer > Exécuter et tapez dans l'invite de commande cmd. Une console s'ouvre, déplacez-vous dans le dossier dans lequel vous souhaitez créer votre projet grâce à la commande cd, suivie d'un chemin. Exemple :

cd C:\Mes Documents\Utilisateur\

Sous Mac OS X et Linux, lancez tout simplement l'application Terminal (elle peut parfois également être nommée Console sous Linux), et déplacez-vous dans le dossier dans lequel vous souhaitez créer votre projet, également à l'aide de la commande cd.
Exemple :

cd /home/mathx/Projets/

Tout au long du tutoriel, nous utiliserons un blog sur les bonnes crêpes bretonnes comme exemple. Ainsi, appelons notre projet crepes_bretonnes (seuls les caractères alphanumériques et underscores sont autorisés pour le nom du projet) et créons-le grâce à la commande suivante :

django-admin.py startproject crepes_bretonnes

Un nouveau dossier nommé crepes_bretonnes est apparu et possède la structure suivante :

crepes_bretonnes/
        manage.py
        crepes_bretonnes/
                __init__.py
                settings.py
                urls.py
                wsgi.py

Il s'agit de votre projet.

Dans le dossier principal crepes_bretonnes, nous retrouvons deux éléments : un fichier manage.py et un autre sous-dossier nommé également crepes_bretonnes. Créez dans le dossier principal un dossier nommé templates, lequel contiendra vos templates HTML.

Le sous-dossier contient quatre fichiers Python, à savoir settings.py, urls.py, wsgi.py et __init__.py. Ne touchez surtout pas à ces deux derniers fichiers, ils n'ont pas pour but d'être modifiés ! Les deux autres fichiers ont des noms plutôt éloquents : settings.py contiendra la configuration de votre projet, tandis que urls.py rassemblera toutes les URL de votre site web et la liste des fonctions à appeler pour chaque URL. Nous reviendrons sur ces deux fichiers plus tard.

Ensuite, le fichier manage.py est en quelque sorte un raccourci local de la commande django-admin.py qui prend en charge la configuration de votre projet. Vous pouvez désormais oublier la commande django-admin.py, elle ne sert en réalité qu'à créer des projets, tout le reste se fait via manage.py. Bien évidemment, n'éditez pas ce fichier non plus.

Votre projet étant créé, pour vous assurer que tout a été correctement effectué jusqu'à maintenant, vous pouvez lancer le serveur de développement via la commande python manage.py runserver :

$ python manage.py runserver
Validating models...

0 errors found
March 04, 2013 - 20:31:54
Django version 1.5, using settings 'crepes_bretonnes.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Cette console vous donnera des informations, des logs (quelle page a été accédée et par qui) et les exceptions de Python lancées en cas d'erreur lors du développement. Par défaut, l'accès au site de développement se fait via l'adresse http://localhost:8000. Vous devriez obtenir quelque chose comme la figure suivante dans votre navigateur :

Votre première page Django
Votre première page Django

Si ce n'est pas le cas, assurez-vous d'avoir bien respecté toutes les étapes précédentes !

Au passage, manage.py propose bien d'autres sous-commandes, autres que runserver. Une petite liste est fournie avec la sous-commande help :

python manage.py help

Toutes ces commandes sont expliquées dans une annexe, donc nous vous invitons à la survoler de temps en temps, au fur et à mesure que vous avancez dans ce cours, et nous reviendrons sur certaines d'entre elles dans certains chapitres. Il s'agit là d'un outil très puissant qu'il ne faut surtout pas sous-estimer. Le développeur Django y a recours quasiment en permanence, d'où l'intérêt de savoir le manier correctement.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Gestion d'un projet Configurez votre projet

Configurez votre projet

Créons notre premier projet Créons notre première application

Configurez votre projet

Avant de commencer à écrire des applications Django, configurons le projet que nous venons de créer. Ouvrez le fichier settings.py dont nous avons parlé tout à l'heure. Il s'agit d'un simple fichier Python avec une liste de variables que vous pouvez modifier à votre guise. Voici les plus importantes :

DEBUG = True
TEMPLATE_DEBUG = DEBUG

Ces deux variables permettent d'indiquer si votre site web est en mode « debug » ou pas. Le mode de débogage affiche des informations pour déboguer vos applications en cas d'erreur. Ces informations affichées peuvent contenir des données sensibles de votre fichier de configuration. Ne mettez donc jamais DEBUG = True en production !

Le tuple ADMINS, qui est par défaut vide, est censé contenir quelques informations à propos des gestionnaires du site (nom et adresse e-mail). L'adresse e-mail servira notamment à envoyer les erreurs rencontrées par les visiteurs de votre site en production. En voici un exemple :

ADMINS = (
  ('Maxime Lorant', '[email protected]'),
  ('Mathieu Xhonneux', '[email protected]'),
)

La configuration de la base de données se fait dans le dictionnaire DATABASES. Nous conseillons pour le développement local l'utilisation d'une base de données SQLite. L'avantage de SQLite comme gestionnaire de base de données pour le développement est simple : il ne s'agit que d'un simple fichier. Il n'y a donc pas besoin d'installer un service à part comme MySQL ; Python et Django se chargent de tout. Si vous n'avez aucune idée de ce qu'est réellement une base de données SQLite, n'ayez aucune crainte, le prochain chapitre vous expliquera en détail en quoi elles consistent et comment elles fonctionnent.

Voici la configuration nécessaire pour l'utilisation de SQLite :

DATABASES = {
  'default': {
    'ENGINE': 'django.db.backends.sqlite3',
    'NAME': 'database.sql', 
    'USER': '', 
    'PASSWORD': '',
    'HOST': '',
    'PORT': '',
  }
}

Modifiez le fuseau horaire et la langue de l'administration :

TIME_ZONE = 'Europe/Paris'
LANGUAGE_CODE = 'fr-FR'

TEMPLATE_DIRS est un simple tuple contenant les listes des dossiers vers les templates. Nous avons créé un dossier templates à la racine de notre projet tout à l'heure, incluons-le donc ici :

TEMPLATE_DIRS = (
  "/home/crepes/crepes_bretonnes/templates/"
)

Finalement, pour des raisons pratiques qui seront explicitées par la suite, ajoutons une option qui permet de compléter automatiquement les URL par un slash (« / ») à la fin de celles-ci, si celui-ci n'est pas déjà présent. Vous en comprendrez l'utilité lorsque nous aborderons le routage d'URL :

APPEND_SLASH = True  # Ajoute un slash en fin d'URL

Voilà ! Les variables les plus importantes ont été expliquées. Pour que ce ne soit pas indigeste, nous n'avons pas tout traité, il en reste en effet beaucoup d'autres. Nous reviendrons sur certains paramètres plus tard. En attendant, si une variable vous intrigue, n'hésitez pas à lire le commentaire (bien qu'en anglais) à côté de la déclaration et à vous référer à la documentation en ligne.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Créons notre premier projet Créons notre première application

Créons notre première application

Configurez votre projet Les bases de données et Django

Créons notre première application

Comme nous l'avons expliqué précédemment, un projet se compose de plusieurs applications, chacune ayant un but bien précis (système d'actualités, galerie photos…). Pour créer une application dans un projet, le fonctionnement est similaire à la création d'un projet : il suffit d'utiliser la commande manage.py avec startapp, à l'intérieur de votre projet. Pour notre site sur les crêpes bretonnes, créons un blog pour publier nos nouvelles recettes :

python manage.py startapp blog

Comme pour startproject, startapp crée un dossier avec plusieurs fichiers à l'intérieur. La structure de notre projet ressemble à ceci :

crepes_bretonnes/
        manage.py
        crepes_bretonnes/
                __init__.py
                settings.py
                urls.py
                wsgi.py
        blog/
                __init__.py
                models.py
                tests.py
                views.py

Les noms des fichiers sont relativement évidents :

Dernière petite chose, il faut ajouter cette application au projet. Pour que Django considère le sous-dossier blog comme une application, il faut donc l'ajouter dans la configuration.

Retournez dans settings.py, et cherchez la variable INSTALLED_APPS. Tout en conservant les autres applications installées, ajoutez une chaîne de caractères avec le nom de votre application. Au passage, décommentez l'application django.contrib.admin, il s'agit de l'application qui génère automatiquement l'administration et dont nous nous occuperons plus tard.

Votre variable devrait ressembler à quelque chose comme ceci :

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'blog',
)
En résumé
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Configurez votre projet Les bases de données et Django

Les bases de données et Django

Créons notre première application Une base de données, c'est quoi ?

Pour que vous puissiez enregistrer les données de vos visiteurs, l'utilisation d'une base de données s'impose. Nous allons dans ce chapitre expliquer le fonctionnement d'une base de données, le principe des requêtes SQL et l'interface que Django propose entre les vues et les données enregistrées. À la fin de ce chapitre, vous aurez assez de connaissances théoriques pour comprendre par la suite le fonctionnement des modèles.

Une base de données, c'est quoi ?

Les bases de données et Django Le langage SQL et les gestionnaires de base de données

Une base de données, c'est quoi ?

Imaginez que vous souhaitiez classer sur papier la liste des films que vous possédez à la maison. Un film a plusieurs caractéristiques : le titre, le résumé, le réalisateur, les acteurs principaux, le genre, l'année de sortie, une appréciation, etc. Il est important que votre méthode de classement permette de différencier très proprement ces caractéristiques. De même, vous devez être sûrs que les caractéristiques que vous écrivez sont correctes et homogènes. Si vous écrivez la date de sortie une fois en utilisant des chiffres, puis une autre fois en utilisant des lettres, vous perdez en lisibilité et risquez de compliquer les choses.

Il existe plusieurs méthodes de classement pour trier nos films, mais la plus simple et la plus efficace (et à laquelle vous avez sûrement dû penser) est tout simplement un tableau ! Pour classer nos films, les colonnes du tableau renseignent les différentes caractéristiques qu'un film peut avoir, tandis que les lignes représentent toutes les caractéristiques d'un même film. Par exemple :

Titre

Réalisateur

Année de sortie

Note (sur 20)

Pulp Fiction

Quentin Tarantino

1994

20

Inglorious Basterds

Quentin Tarantino

2009

18

Holy Grail

Monty Python

1975

19

Fight Club

David Fincher

1999

20

Life of Brian

Monty Python

1979

17

Le classement par tableau est très pratique et simple à comprendre. Les bases de données s'appuient sur cette méthode de tri pour enregistrer et classer les informations que vous spécifierez.

Une base de données peut contenir plusieurs tableaux, chacun servant à enregistrer un certain type d'élément. Par exemple, dans votre base, vous pourriez avoir un tableau qui recensera vos utilisateurs, un autre pour les articles, encore un autre pour les commentaires, etc.

Nous avons évoqué un autre point important de ces bases de données, avec l'exemple de la date de sortie. Il faut en effet que toutes les données dans une colonne soient homogènes. Autrement dit, elles doivent avoir un même type de données : entier, chaîne de caractères, texte, booléen, date… Si vous enregistrez un texte dans la colonne Note, votre code vous renverra une erreur. Dès lors, chaque fois que vous irez chercher des données dans une table, vous serez sûrs du type des variables que vous obtiendrez.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les bases de données et Django Le langage SQL et les gestionnaires de base de données

Le langage SQL et les gestionnaires de base de données

Une base de données, c'est quoi ? La magie des ORM

Le langage SQL et les gestionnaires de base de données

Il existe plusieurs programmes qui s'occupent de gérer des bases de données. Nous les appelons, tout naturellement, des gestionnaires de bases de données (ou « SGBD » pour « systèmes de gestion de bases de données »). Ces derniers s'occupent de tout : création de nouvelles tables, ajout de nouvelles entrées dans une table, mise à jour des données, renvoi des entrées déjà enregistrées, etc. Il y a énormément de SGBD, chacun avec des caractéristiques particulières. Néanmoins, ils se divisent en deux grandes catégories : les bases de données SQL et les bases de données non-SQL. Nous allons nous intéresser à la première catégorie (celle que Django utilise).

Les gestionnaires de bases de données SQL sont les plus populaires et les plus utilisés pour le moment. Ceux-ci reprennent l'utilisation du classement par tableau tel que nous l'avons vu. L'acronyme « SQL » signifie « Structured Query Language », ou en français « langage de requêtes structurées ». En effet, lorsque vous souhaitez demander au SGBD toutes les entrées d'une table, vous devez communiquer avec le serveur (le programme qui sert les données) dans un langage qu'il comprend. Ainsi, si pour commander un café vous devez parler en français, pour demander les données au gestionnaire vous devez parler en SQL.

Voici un simple exemple de requête SQL qui renvoie toutes les entrées de la table films dont le réalisateur doit être Quentin Tarantino et qui sont triées par date de sortie :

SELECT titre, annee_sortie, note FROM films WHERE realisateur="Quentin Tarantino" ORDER BY annee_sortie

On a déjà vu plus simple, mais voilà comment communiquent un serveur SQL et un client. Il existe bien d'autres commandes (une pour chaque type de requête : sélection, mise à jour, suppression…) et chaque commande possède ses paramètres.

Heureusement, tous les SGBD SQL parlent à peu près le même SQL, c'est-à-dire qu'une requête utilisée avec un gestionnaire fonctionnera également avec un autre. Néanmoins, ce point est assez théorique, car même si les requêtes assez basiques marchent à peu près partout, les requêtes plus pointues et avancées commencent à diverger selon le SGBD, et si un jour vous devez changer de gestionnaire, nul doute que vous devrez réécrire certaines requêtes. Django a une solution pour ce genre de situations, nous verrons cela par la suite.

Voici quelques gestionnaires SQL bien connus (dont vous avez sûrement déjà dû voir le nom quelque part) :

Lors de la configuration de votre projet Django dans le chapitre précédent, nous vous avons conseillé d'utiliser SQLite. Pourquoi ? Car contrairement aux autres SGBD qui ont besoin d'un serveur lancé en permanence pour traiter les données, une base de données SQLite consiste en un simple fichier. C'est la bibliothèque Python (nommée sqlite3) qui se chargera de modifier et renvoyer les données de la base. C'est très utile en développement, car il n'y a rien à installer, mais en production mieux vaut utiliser un SGBD plus performant comme MySQL.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Une base de données, c'est quoi ? La magie des ORM

La magie des ORM

Le langage SQL et les gestionnaires de base de données Le principe des clés étrangères

La magie des ORM

Apprendre le langage SQL et écrire ses propres requêtes est quelque chose d'assez difficile et contraignant lorsque nous débutons. Cela prend beaucoup de temps et est assez rébarbatif. Heureusement, Django propose un système pour bénéficier des avantages d'une base de données SQL sans devoir écrire ne serait-ce qu'une seule requête SQL !

Ce type de système s'appelle ORM pour « object-relationnal mapping ». Derrière ce nom un peu barbare se cache un fonctionnement simple et très utile. Lorsque vous créez un modèle dans votre application Django, le framework va automatiquement créer une table adaptée dans la base de données qui permettra d'enregistrer les données relatives au modèle.

Sans entrer dans les détails (nous verrons cela après), voici un modèle simple qui reviendra par la suite :

class Article(models.Model):
   titre = models.CharField(max_length=100)
   auteur = models.CharField(max_length=42)
   contenu = models.TextField(null=True)
   date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de parution")

À partir de ce modèle, Django va créer une table blog_article (« blog » étant le nom de l'application dans laquelle le modèle est ajouté) dont les champs seront titre, auteur, contenu et date. Chaque champ a son propre type (tel que défini dans le modèle), et ses propres paramètres. Tout cela se fait, encore une fois, sans écrire la moindre requête SQL.

La manipulation de données est tout aussi simple bien évidemment. Le code suivant…

Article(titre="Bonjour", auteur="Maxime", contenu="Salut").save()

… créera une nouvelle entrée dans la base de données. Notez la relation qui se crée : chaque instance du modèle Article qui se crée correspond à une entrée dans la table SQL. Toute manipulation des données dans la base se fait depuis des objets Python, ce qui est bien plus intuitif et simple.

De la même façon, il est possible d'obtenir toutes les entrées de la table. Ainsi le code suivant…

Article.objects.all()

… renverra des instances d'Article, une pour chaque entrée dans la table, comme le schématise la figure suivante :

Fonctionnement de l'ORM de Django
Fonctionnement de l'ORM de Django

Pour conclure, l'ORM est un système très flexible de Django qui s'insère parfaitement bien dans l'architecture MVT que nous avons décrite précédemment.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Le langage SQL et les gestionnaires de base de données Le principe des clés étrangères

Le principe des clés étrangères

La magie des ORM Votre première page grâce aux vues

Le principe des clés étrangères

Pour terminer ce chapitre, nous allons aborder une dernière notion théorique relative aux bases de données SQL, il s'agit des clés étrangères (ou Foreign Keys en anglais).

Reprenons notre exemple de tout à l'heure : nous avons une table qui recense plusieurs films. Imaginons maintenant que nous souhaitions ajouter des données supplémentaires, qui ne concernent pas les films mais les réalisateurs. Nous voudrions par exemple ajouter le pays d'origine et la date de naissance des réalisateurs. Étant donné que certains réalisateurs reviennent plusieurs fois, il serait redondant d'ajouter les caractéristiques des réalisateurs dans les caractéristiques des films. La bonne solution ? Créer une nouvelle table qui recensera les réalisateurs et ajouter un lien entre les films et les réalisateurs.

Lorsque Django crée une nouvelle table depuis un modèle, il va ajouter un autre champ qui n'est pas dans les attributs de la classe. Il s'agit d'un champ tout simple nommé ID (pour « identifiant », synonyme ici de « clé »), qui contiendra un certain nombre unique à l'entrée, et qui va croissant au fil des entrées. Ainsi, le premier réalisateur ajouté aura l'identifiant 1, le deuxième l'identifiant 2, etc.

Voici donc à quoi ressemblerait notre table des réalisateurs :

ID

Nom

Pays d'origine

Date de naissance

1

Quentin Tarantino

USA

1963

2

David Fincher

USA

1962

3

Monty Python

Grande Bretagne

1969

Jusqu'ici, rien de spécial à part la nouvelle colonne ID introduite précédemment. En revanche, dans la table recensant les films, une colonne a été modifiée :

Titre

Réalisateur

Année de sortie

Note (sur 20)

Pulp Fiction

1

1994

20

Inglorious Basterds

1

2009

18

Holy Grail

3

1975

19

Fight Club

2

1999

20

Life of Brian

3

1979

17

Désormais, les noms des réalisateurs sont remplacés par des nombres. Ceux-ci correspondent aux identifiants de la table des réalisateurs. Si nous souhaitons obtenir le réalisateur du film Fight Club, il faut aller regarder dans la table réalisateurs et sélectionner l'entrée ayant l'identifiant 2. Nous pouvons dès lors regarder le nom du réalisateur : nous obtenons bien à nouveau David Fincher, et les données supplémentaires (date de naissance et pays d'origine) sont également accessibles.

Cette méthode de clé étrangère (car la clé vient d'une autre table) permet de créer simplement des liens entre des entrées dans différents tableaux. L'ORM de Django gère parfaitement cette méthode. Vous n'aurez probablement jamais besoin de l'identifiant pour gérer des liaisons, Django s'en occupera et renverra directement l'objet de l'entrée associée.

En résumé
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

La magie des ORM Votre première page grâce aux vues

Votre première page grâce aux vues

Le principe des clés étrangères Hello World !

Dans ce chapitre, nous allons créer notre première page web avec Django. Pour ce faire, nous verrons comment créer une vue dans une application et la rendre accessible depuis une URL. Une fois cela fait, nous verrons comment organiser proprement nos URL afin de rendre le code plus propre et structuré. Nous aborderons ensuite deux cas spécifiques des URL, à savoir la gestion de paramètres et de variables dans celles-ci, et les redirections, messages d'erreur, etc.

Cette partie est fondamentale pour aborder la suite et comprendre le fonctionnement du framework en général. Autrement dit, nous ne pouvons que vous conseiller de bien vous accrocher tout du long !

Hello World !

Votre première page grâce aux vues Routage d'URL : comment j'accède à ma vue ?

Hello World !

Commençons enfin notre blog sur les bonnes crêpes bretonnes. En effet, au chapitre précédent, nous avons créé une application « blog » dans notre projet, il est désormais temps de se mettre au travail !

Pour rappel, comme vu dans la théorie, chaque vue se doit d'être associée au minimum à une URL. Avec Django, une vue est représentée par une fonction définie dans le fichier views.py. Cette fonction va généralement récupérer des données dans les modèles (ce que nous verrons plus tard) et appeler le bon template pour générer le rendu HTML adéquat. Par exemple, nous pourrions donner la liste des 10 derniers articles de notre blog au moteur de templates, qui se chargera de les insérer dans une page HTML finale, qui sera renvoyée à l'utilisateur.

Pour débuter, nous allons réaliser quelque chose de relativement simple : une page qui affichera « Bienvenue sur mon blog ! ».

La gestion des vues

Chaque application possède son propre fichier views.py, regroupant l'ensemble des fonctions que nous avons introduites précédemment. Comme tout bon blog, le nôtre possèdera plusieurs vues qui rempliront diverses tâches, comme l'affichage d'un article par exemple.

Commençons à travailler dans blog/views.py. Par défaut, Django a généré gentiment ce fichier avec le commentaire suivant :

# Create your views here.

Pour éviter tout problème par la suite, indiquons à l'interpréteur Python que le fichier sera en UTF-8, afin de prendre en charge les accents. En effet, Django gère totalement l'UTF-8 et il serait bien dommage de ne pas l'utiliser. Insérez ceci comme première ligne de code du fichier :

#-*- coding: utf-8 -*-

Désormais, nous pouvons créer une fonction qui remplira le rôle de la vue. Bien que nous n'ayons vu pour le moment ni les modèles, ni les templates, il est tout de même possible d'écrire une vue, mais celle-ci restera basique. En effet, il est possible d'écrire du code HTML directement dans la vue et de le renvoyer au client :

#-*- coding: utf-8 -*-
from django.http import HttpResponse

def home(request):
  text = """<h1>Bienvenue sur mon blog !</h1>
            <p>Les crêpes bretonnes ça tue des mouettes en plein vol !</p>"""
  return HttpResponse(text)

Ce code se divise en trois parties :

Par la suite, ne renvoyez jamais du code HTML directement depuis la vue comme nous le faisons ici. Passez toujours par des templates, ce que nous introduirons au chapitre suivant. Il s'agit de respecter l'architecture du framework dont nous avons parlé dans la partie précédente afin de bénéficier de ses avantages (la structuration du code notamment). Nous n'avons utilisé cette méthode que dans un but pédagogique et afin de montrer les choses une par une.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Votre première page grâce aux vues Routage d'URL : comment j'accède à ma vue ?

Routage d'URL : comment j'accède à ma vue ?

Hello World ! Organiser proprement vos URL

Routage d'URL : comment j'accède à ma vue ?

Nous avons désormais une vue opérationnelle, il n'y a plus qu'à l'appeler depuis une URL. Mais comment ? En effet, nous n'avons pas encore défini vers quelle URL pointait cette fonction. Pour ce faire, il faut modifier le fichier urls.py de votre projet (ici crepes_bretonnes/urls.py). Par défaut, ce fichier contient une aide basique :

from django.conf.urls import patterns, include, url

# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'crepes.views.home', name='home'),
    # url(r'^crepes/', include('crepes.foo.urls')),

    # Uncomment the admin/doc line below to enable admin documentation:
    # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    # url(r'^admin/', include(admin.site.urls)),
)

Quand un utilisateur appelle une page de votre site, la requête est directement prise en charge par le contrôleur de Django qui va chercher à quelle vue correspond cette URL. En fonction de l'ordre de définition dans le fichier précédent, la première vue qui correspond à l'URL demandée sera appelée, et elle retournera donc la réponse HTML au contrôleur (qui, lui, la retournera à l'utilisateur). Si aucune URL ne correspond à un schéma que vous avez défini, alors Django renverra une page d'erreur 404. Le schéma d'exécution est celui de la figure suivante.

Schéma d'exécution d'une requête (nous travaillons pour le moment sans templates et sans modèles)
Schéma d'exécution d'une requête (nous travaillons pour le moment sans templates et sans modèles)

Occupons-nous uniquement du tuple urlpatterns, qui permet de définir les associations entre URL et vues. Une association de routage basique se définit par un sous-tuple composé des éléments suivants :

Par exemple, en reprenant la vue définie tout à l'heure, si nous souhaitons que celle-ci soit accessible depuis l'URL http://www.crepes-bretonnes.com/accueil, il suffit de rajouter cette règle dans votre urlpatterns :

urlpatterns = patterns('',
    url(r'^accueil/$', 'blog.views.home'),
)

Qu'est-ce que c'est, tous ces caractères bizarres dans l'URL ?

Il s'agit d'expressions régulières (ou « regex ») qui permettent de créer des URL plus souples. Il est généralement conseillé de maîtriser au moins les bases des regex pour pouvoir écrire des URL correctes. Dans ce cas-ci :

Bien évidemment, toute expression régulière compatible avec le module re de Python sera compatible ici aussi.

Concernant le lien vers la vue, il s'agit du même type de lien utilisé lors d'une importation de module. Ici :

Grâce à cette règle, Django saura que lorsqu'un client demande la page http://www.crepes-bretonnes.com/accueil, il devra appeler la vue blog.views.home.

Enregistrez les modifications, lancez le serveur de développement Django et laissez-le tourner (pour rappel : python manage.py runserver), et rendez-vous sur http://localhost:8000/accueil/. Vous devriez obtenir quelque chose comme la figure suivante.

L'affichage de votre première vue
L'affichage de votre première vue

Si c'est le cas, félicitations, vous venez de créer votre première vue !

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Hello World ! Organiser proprement vos URL

Organiser proprement vos URL

Routage d'URL : comment j'accède à ma vue ? Passer des arguments à vos vues

Organiser proprement vos URL

Dans la partie précédente, nous avions parlé de deux avantages importants de Django : la réutilisation d'applications et la structuration du code. Sauf qu'évidemment, un problème se pose avec l'utilisation des URL que nous avons faites : si nous avons plusieurs applications, toutes les URL de celles-ci iraient dans urls.py du projet, ce qui compliquerait nettement la réutilisation d'une application et ne structure en rien votre code.

En effet, il faudrait sans cesse recopier toutes les URL d'une application en l'incluant dans un projet, et une application complexe peut avoir des dizaines d'URL, ce qui ne facilite pas la tâche du développeur. Sans parler de la problématique qui survient lorsqu'il faut retrouver la bonne vue parmi la centaine de vues déjà écrites. C'est pour cela qu'il est généralement bien vu de créer dans chaque application un fichier également nommé urls.py et d'inclure ce dernier par la suite dans le fichier urls.py du projet.

Comment procède-t-on ?

Tout d'abord, il faut créer un fichier urls.py dans le dossier de votre application, ici blog. Ensuite, il suffit d'y réécrire l'URL que nous avons déjà écrite précédemment (ne pas oublier l'importation des modules nécessaires !) :

from django.conf.urls import patterns, url

urlpatterns = patterns('',
    url(r'^accueil/$', 'blog.views.home'),
)

Et c'est déjà tout pour blog/urls.py !

Maintenant, retournons à crepes_bretonnes/urls.py. Nous pouvons y enlever la règle réécrite dans blog/urls.py. Il ne devrait donc plus rester grand-chose. L'importation des règles de blogs/urls.py est tout aussi simple, il suffit d'utiliser la fonction include de django.conf.urls et d'ajouter ce sous-tuple à urlpatterns :

url(r'^blog/', include('blog.urls'))

En quoi consiste l'URL ^blog/ ici ?

Cette URL (en réalité portion d'URL), va précéder toutes les URL incluses. Autrement dit, nous avions une URL /accueil qui envoyait vers la vue blog.views.home, désormais celle-ci sera accessible depuis /blog/accueil. Et cela vaut pour toutes les futures URL importées. Cependant, rien ne vous empêche de laisser cette chaîne de caractères vide (/accueil restera /accueil), mais il s'agit d'une bonne solution pour structurer vos URL.

Nous avons scindé nos URL dans un fichier urls.py pour chaque application. Cependant, nous allons bientôt ajouter d'autres URL plus complexes dans notre blog/urls.py. Toutes ces URL seront routées vers des vues de blog.views. Au final, la variable urlpatterns de notre blog/urls.py risque de devenir longue :

urlpatterns = patterns('',
    url(r'^accueil/$', 'blog.views.home'),
    url(r'^truc/$', 'blog.views.truc'),
    url(r'^chose/$', 'blog.views.chose'),
    url(r'^machin/$', 'blog.views.machin'),
    url(r'^foo/$', 'blog.views.foo'),
    url(r'^bar/$', 'blog.views.bar'),
)

Maintenant, imaginez que votre application « blog » change de nom, vous allez devoir réécrire tous les chemins vers vos vues ! Pour éviter de devoir modifier toutes les règles une à une, il est possible de spécifier un module par défaut qui contient toutes les vues. Pour ce faire, il faut utiliser le premier élément de notre tuple qui est resté une chaîne de caractères vide jusqu'à maintenant :

urlpatterns = patterns('blog.views',
    url(r'^accueil/$', 'home'),
    url(r'^truc/$', 'truc'),
    url(r'^chose/$', 'chose'),
    url(r'^machin/$', 'machin'),
    url(r'^foo/$', 'foo'),
    url(r'^bar/$', 'bar'),
)

Tout est beaucoup plus simple et facilement éditable. Le module par défaut ici est blog.views, car toutes les vues viennent de ce fichier-là ; cela est désormais possible, car nous avons scindé notre urls.py principal en plusieurs urls.py propres à chaque application.

Finalement, notre blog/urls.py ressemblera à ceci :

from django.conf.urls import patterns, url

urlpatterns = patterns('blog.views',
    url(r'^accueil/$', 'home'),
)

Ne négligez pas cette solution, utilisez-la dès maintenant ! Il s'agit d'une excellente méthode pour structurer votre code, parmi tant d'autres que Django offre. Pensez aux éventuels développeurs qui pourraient maintenir votre projet après vous et qui n'ont pas envie de se retrouver avec une structure proche de l'anarchie.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Routage d'URL : comment j'accède à ma vue ? Passer des arguments à vos vues

Passer des arguments à vos vues

Organiser proprement vos URL Des réponses spéciales

Passer des arguments à vos vues

Nous avons vu comment lier des URL à des vues et comment les organiser. Cependant, un besoin va bientôt se faire sentir : pouvoir passer des paramètres dans nos adresses directement. Si vous observez les adresses du site Instagram (qui est basé sur Django pour rappel), le lien vers une photo est construit ainsi : http://instagr.am/p/************** est une suite de caractères alphanumériques. Cette suite représente en réalité l'identifiant de la photo sur le site et permet à la vue de récupérer les informations en relation avec cette photo.

Pour passer des arguments dans une URL, il suffit de capturer ces arguments directement depuis les expressions régulières. Par exemple, si nous souhaitons sur notre blog pouvoir accéder à un certain article via l'adresse /blog/article/**** sera l'identifiant de l'article (un nombre unique), il suffit de fournir le routage suivant dans votre urls.py :

urlpatterns = patterns('blog.views',
    url(r'^accueil/$', 'home'), # Accueil du blog
    url(r'^article/(\d+)/$', 'view_article'), # Vue d'un article
    url(r'^articles/(\d{4})/(\d{2})/$', 'list_articles'), # Vue des articles d'un mois précis
)

Lorsque l'URL /blog/article/42 est demandée, Django regarde le routage et exécute la fonction view_article, en passant en paramètre 42. Autrement dit, Django appelle la vue de cette manière : view_article(request, 42). Voici un exemple d'implémentation :

def view_article(request, id_article):
    """ Vue qui affiche un article selon son identifiant (ou ID, ici un numéro). Son ID est le second paramètre de la fonction 
        (pour rappel, le premier paramètre est TOUJOURS la requête de l'utilisateur) """
   
    text = "Vous avez demandé l'article n°{0} !".format(id_article)
    return HttpResponse(text)

Il faut cependant faire attention à l'ordre des paramètres dans l'URL afin qu'il corresponde à l'ordre des paramètres de la fonction. En effet, lorsque nous souhaitons obtenir la liste des articles d'un mois précis, selon la troisième règle que nous avons écrite, il faudrait accéder à l'URL suivante pour le mois de juin 2012 : /blog/articles/2012/06.

Cependant, si nous souhaitons changer l'ordre des paramètres de l'URL pour afficher le mois, et ensuite l'année, celle-ci deviendrait /blog/articles/06/2012. Il faudra donc modifier l'ordre des paramètres dans la déclaration de la fonction en conséquence.

Pour éviter cette lourdeur et un bon nombre d'erreurs, il est possible d'associer une variable de l'URL à un paramètre de la vue. Voici la démarche :

urlpatterns = patterns('blog.views',
    url(r'^home/$', 'home'), # Accueil du blog
    url(r'^article/(?P<id_article>\d+)/$', 'view_article'), # Vue d'un article
    url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'list_articles'), # Vue des articles d'un mois précis
)

Et la vue correspondante :

def list_articles(request, month, year):
    """ Liste des articles d'un mois précis. """

    text = "Vous avez demandé les articles de {0} {1}.".format(month, year)
    return HttpResponse(text)

Dans cet exemple, mois et année (month et year) ne sont pas dans le même ordre entre le urls.py et le views.py, mais Django s'en occupe et règle l'ordre des arguments en fonction des noms qui ont été donnés dans le urls.py. En réalité, le framework va exécuter la fonction de cette manière :

list_articles(request, year=2012, month=6)

Il faut juste s'assurer que les noms de variables donnés dans le fichier urls.py coïncident avec les noms donnés dans la déclaration de la vue, sans quoi Python retournera une erreur.

Pour terminer, sachez qu'il est toujours possible de passer des paramètres GET. Par exemple : http://www.crepes-bretonnes.com/blog/article/1337?ref=twitter. Django tentera de trouver le pattern correspondant en ne prenant en compte que ce qui est avant les paramètres GET, c'est-à-dire /blog/article/1337/. Les paramètres passés par la méthode GET sont bien évidemment récupérables, ce que nous verrons plus tard.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Organiser proprement vos URL Des réponses spéciales

Des réponses spéciales

Passer des arguments à vos vues Les templates

Des réponses spéciales

Jusqu'ici, nous avons vu comment renvoyer une page HTML standard. Cependant, il se peut que nous souhaitions renvoyer autre chose que du HTML : une erreur 404 (page introuvable), une redirection vers une autre page, etc.

Simuler une page non trouvée

Parfois, une URL correspond bien à un pattern mais ne peut tout de même pas être considérée comme une page existante. Par exemple, lorsque vous souhaitez afficher un article avec un identifiant introuvable, il est impossible de renvoyer une page, même si Django a correctement identifié l'URL et utilisé la bonne vue. Dans ce cas-là, nous pouvons le faire savoir à l'utilisateur via une page d'erreur 404, qui correspond au code d'erreur indiquant qu'une page n'a pas été trouvée. Pour ce faire, il faut utiliser une exception du framework : Http404. Cette exception, du module django.http, arrête le traitement de la vue, et renvoie l'utilisateur vers une page d'erreur.

Voici un rapide exemple d'une vue compatible avec une des règles de routage que nous avons décrites dans le sous-chapitre précédent :

from django.http import HttpResponse, Http404

def view_article(request, id_article):
    if int(id_article) > 100: #Si l'ID est supérieur à 100, nous considérons que l'article n'existe pas
        raise Http404

    return HttpResponse('<h1>Mon article ici</h1>')

Si à l'appel de la page l'argument id_article est supérieur à 100, la page retournée sera une erreur 404 de Django, visible à la figure suivante. Il est bien entendu possible de personnaliser par la suite cette vue, avec un template, afin d'avoir une page d'erreur qui soit en accord avec le design de votre site, mais cela ne fonctionne uniquement qu'avec DEBUG = False dans le settings.py (en production donc). Si vous êtes en mode de développement, vous aurez toujours une erreur similaire à la figure suivante.

Erreur 404, page introuvable
Erreur 404, page introuvable
Rediriger l'utilisateur

Le second cas que nous allons aborder concerne les redirections. Il arrive que vous souhaitiez rediriger votre utilisateur vers une autre page lorsqu'une action vient de se dérouler, ou en cas d'erreur rencontrée. Par exemple, lorsqu'un utilisateur se connecte, il est souvent redirigé soit vers l'accueil, soit vers sa page d'origine. Une redirection est réalisable avec Django via la méthode redirect qui renvoie un objet HttpResponseRedirect (classe héritant de HttpResponse), qui redirigera l'utilisateur vers une autre URL. La méthode redirect peut prendre en paramètres plusieurs types d'arguments, dont notamment une URL brute (chaîne de caractères) ou le nom d'une vue.

Si par exemple vous voulez que votre vue, après une certaine opération, redirige vos visiteurs vers le Site du Zéro, il faudrait procéder ainsi :

from django.shortcuts import redirect

def list_articles(request, year, month):
    # Il veut des articles ?
    return redirect("http://www.siteduzero.com") # Nous le redirigeons vers le Site du Zéro

N'oubliez pas qu'une URL valide pour accéder à cette vue serait /blog/articles/2005/05.

Cependant, si vous souhaitez rediriger votre visiteur vers une autre page de votre site web, il est plus intéressant de privilégier l'autre méthode, qui permet de garder indépendante la configuration des URL et des vues. Nous devons donc passer en argument le nom de la vue vers laquelle nous voulons rediriger l'utilisateur, avec éventuellement des arguments destinés à celle-ci.

from django.http import HttpResponse, Http404
from django.shortcuts import redirect

def view_article(request, id_article):
    if int(id_article) > 100:
        raise Http404

    return redirect(view_redirection)

def view_redirection(request):
    return HttpResponse(u"Vous avez été redirigé.")
url(r'^redirection/$', 'view_redirection'),

Ici, si l'utilisateur accède à l'URL /blog/article/101, il aura toujours une page 404. Par contre, s'il choisit un ID inférieur à 100, alors il sera redirigé vers la seconde vue, qui affiche un simple message.

Il est également possible de préciser si la redirection est temporaire ou définitive en ajoutant le paramètre permanent=True. L'utilisateur ne verra aucune différence, mais ce sont des détails que les moteurs de recherche prennent en compte lors du référencement de votre site web.

Si nous souhaitions rediriger un visiteur vers la vue view_article définie précédemment par un ID d'article spécifique, il suffirait simplement d'utiliser la méthode redirect ainsi :

return redirect('blog.views.view_article',id_article=42)

Pourquoi est-ce que nous utilisons une chaîne de caractères pour désigner la vue maintenant, au lieu de la fonction elle-même ?

Il est possible d'indiquer une vue de trois manières différentes :

  1. En passant directement la fonction Python, comme nous l'avons vu au début ;

  2. En donnant le chemin vers la fonction, dans une chaîne de caractères (ce qui évite de l'importer si elle se situe dans un autre fichier) ;

  3. En indiquant le nom de la vue tel qu'indiqué dans un urls.py (voir l'exemple suivant).

En réalité, la fonction redirect va construire l'URL vers la vue selon le routage indiqué dans urls.py. Ici, il va générer l'URL /blog/article/42 tout seul et rediriger l'utilisateur vers cette URL. Ainsi, si par la suite vous souhaitez modifier vos URL, vous n'aurez qu'à le faire dans les fichiers urls.py, tout le reste se mettra à jour automatiquement. Il s'agit d'une fonctionnalité vraiment pratique, il ne faut donc jamais écrire d'URL en dur, sauf quand cette méthode est inutilisable (vers des sites tiers par exemple).

Sachez qu'au lieu d'écrire à chaque fois tout le chemin d'une vue ou de l'importer, il est possible de lui assigner un nom plus court et plus facile à utiliser dans urls.py. Par exemple :

url(r'^article/(?P<id_article>\d+)/$', 'view_article', name="afficher_article"),

Notez le paramètre name="afficher_article" qui permet d'indiquer le nom de la vue. Avec ce routage, en plus de pouvoir passer directement la fonction ou le chemin vers celle-ci en argument, nous pouvons faire beaucoup plus court et procéder comme ceci :

return redirect('afficher_article', id_article=42)

Pour terminer, sachez qu'il existe également une fonction qui permet de générer simplement l'URL et s'utilise de la même façon que redirect ; il s'agit de reverse (from django.core.urlresolvers import reverse). Cette fonction ne retournera pas un objet HttpResponseRedirect, mais simplement une chaîne de caractères contenant l'URL vers la vue selon les éventuels arguments donnés. Une variante de cette fonction sera utilisée dans les templates peu après pour générer des liens HTML vers les autres pages du site.

En résumé
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Passer des arguments à vos vues Les templates

Les templates

Des réponses spéciales Lier template et vue

Nous avons vu comment créer une vue et renvoyer du code HTML à l'utilisateur. Cependant, la méthode que nous avons utilisée n'est pas très pratique, le code HTML était en effet intégré à la vue elle-même ! Le code Python et le code HTML deviennent plus difficiles à éditer et à maintenir pour plusieurs raisons :

C'est à cause de ces raisons que tous les frameworks web actuels utilisent un moteur de templates. Les templates sont écrits dans un mini-langage de programmation propre à Django et qui possède des expressions et des structures de contrôle basiques (if/else, boucle for, etc.) que nous appelons des tags. Le moteur transforme les tags qu'il rencontre dans le fichier par le rendu HTML correspondant. Grâce à ceux-ci, il est possible d'effectuer plusieurs actions algorithmiques : afficher une variable, réaliser des conditions ou des boucles, faire des opérations sur des chaînes de caractères, etc.

Lier template et vue

Les templates Affichons nos variables à l'utilisateur

Lier template et vue

Avant d'aborder le cœur même du fonctionnement des templates, retournons brièvement vers les vues. Dans la première partie, nous avons vu que nos vues étaient liées à des templates (et des modèles), comme le montre la figure suivante.

Schéma d'exécution d'une requête
Schéma d'exécution d'une requête

C'est la vue qui se charge de transmettre l'information de la requête au template, puis de retourner le HTML généré au client. Dans le chapitre précédent, nous avons utilisé la méthode HttpResponse(text) pour renvoyer le HTML au navigateur. Cette méthode prend comme paramètre une chaîne de caractères et la renvoie sous la forme d'une réponse HTTP. La question ici est la suivante : comment faire pour appeler notre template, et générer la réponse à partir de celui-ci ? La fonction render a été conçue pour résoudre ce problème.

Nous commençons par un exemple avec une vue qui renvoie juste la date actuelle à l'utilisateur, et son fichier urls.py associé :

from datetime import datetime
from django.shortcuts import render

def tpl(request):
    return render(request, 'blog/tpl.html', {'current_date': datetime.now()})
url(r'^$', 'tpl'),

Cette fonction prend en argument trois paramètres :

  1. La requête initiale, qui a permis de construire la réponse (request dans notre cas) ;

  2. Le chemin vers le template adéquat dans un des dossiers de templates donnés dans settings.py ;

  3. Un dictionnaire reprenant les variables qui seront accessibles dans le template.

Ici, notre template sera tpl.html, dans le sous-dossier blog, et nous aurons accès à une seule variable : current_date qui aura comme valeur la date renvoyée par la fonction datetime.now().

Créons le template correspondant dans le dossier templates/blog/, ici nommé tpl.html :

<h1>Bienvenue sur mon blog</h1>
<p>La date actuelle est : {{ current_date }}</p>

Nous retrouvons current_date, comme passé dans render() ! Si vous accédez à cette page (après lui avoir assigné une URL), le {{ current_date }} est bel et bien remplacé par la date actuelle !

Deuxième exemple : une vue, et son template associé, qui additionne deux nombres donnés dans l'URL.

def addition(request, nombre1, nombre2):	
    total = int(nombre1) + int(nombre2)

    # retourne nombre1, nombre2 et la somme des deux
    return render(request, 'blog/addition.html', locals())
url(r'^addition/(?P<nombre1>\d+)/(?P<nombre2>\d+)/$', 'addition'),
<h1>Ma super calculatrice</h1>
<p>{{ nombre1 }} + {{ nombre2 }}, ça fait <strong>{{ total }}</strong> !<br />
Nous pouvons également calculer la somme dans le template : {{ nombre1|add:nombre2 }}.

La seule différence dans la vue réside dans le deuxième argument donné à render. Au lieu de lui passer un dictionnaire directement, nous faisons appel à la fonction locals() qui va retourner un dictionnaire contenant toutes les variables locales de la fonction depuis laquelle locals() a été appelée. Les clés seront les noms de variables (par exemple total), et les valeurs du dictionnaire seront tout simplement… les valeurs des variables de la fonction ! Ainsi, si nombre1 valait 42, la valeur nombre1 du dictionnaire vaudra elle aussi 42.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les templates Affichons nos variables à l'utilisateur

Affichons nos variables à l'utilisateur

Lier template et vue Manipulons nos données avec les tags

Affichons nos variables à l'utilisateur

Affichage d'une variable

Comme nous l'avons déjà expliqué, la vue transmet au template les données destinées à l'utilisateur. Ces données correspondent à des variables classiques de la vue. Nous pouvons les afficher dans le template grâce à l'expression {{ }} qui prend à l'intérieur des accolades un argument (on pourrait assimiler cette expression à une fonction), le nom de la variable à afficher. Le nom des variables est également limité aux caractères alphanumériques et aux underscores.

Bonjour {{ pseudo }}, nous sommes le {{ date }}.

Ici, nous considérons que la vue a transmis deux variables au template : pseudo et date. Ceux-ci seront affichés par le moteur de template. Si pseudo vaut « Zozor » et date « 28 décembre », le moteur de templates affichera « Bonjour Zozor, nous sommes le 28 décembre. ».

Si jamais la variable n'est pas une chaîne de caractères, le moteur de templates utilisera la méthode __str__ de l'objet pour l'afficher. Par exemple, les listes seront affichés sous la forme ['element 1', 'element 2'…], comme si vous demandiez son affichage dans une console Python. Il est possible d'accéder aux attributs d'un objet comme en Python, en les juxtaposant avec un point. Plus tard, nos articles de blog seront représentés par des objets, avec des attributs titre, contenu, etc. Pour y accéder, la syntaxe sera la suivante :

{# Nous supposons que notre vue a fourni un objet nommé article contenant les attributs titre, auteur et contenu #}
<h2>{{ article.titre }}</h2>
<p><em>Article publié par {{ article.auteur }}</em></p>

<p>{{ article.contenu }}</p>
Les filtres

Lors de l'affichage des données, il est fréquent de devoir gérer plusieurs cas. Les filtres permettent de modifier l'affichage en fonction d'une variable, sans passer par la vue. Prenons un exemple concret : sur la page d'accueil des sites d'actualités, le texte des dernières nouvelles est généralement tronqué, seul le début est affiché. Pour réaliser la même chose avec Django, nous pouvons utiliser un filtre qui limite l'affichage aux 80 premiers mots de notre article :

{{ texte|truncatewords:80 }}

Ici, le filtre truncatewords (qui prend comme paramètre un nombre, séparé par un deux-points) est appliqué à la variable texte. À l'affichage, cette dernière sera tronquée et l'utilisateur ne verra que les 80 premiers mots de celle-ci.

Ces filtres ont pour but d'effectuer des opérations de façon claire, afin d'alléger les vues, et ne marchent que lorsqu'une variable est affichée (avec la structure {{ }} donc). Il est par exemple possible d'accorder correctement les phrases de votre site avec le filtre pluralize :

Vous avez {{ nb_messages }} message{{ nb_messages|pluralize }}.

Dans ce cas, un « s » sera ajouté si le le nombre de messages est supérieur à 1. Il est possible de passer des arguments au filtre afin de coller au mieux à notre chère langue française :

Il y a {{ nb_chevaux }} chev{{ nb_chevaux|pluralize:"al,aux" }} dans l'écurie.

Ici, nous aurons « cheval » si nb_chevaux est égal à 1 et « chevaux » pour le reste.

Et un dernier pour la route : imaginons que vous souhaitiez afficher le pseudo du membre connecté, ou le cas échéant « visiteur ». Il est possible de le faire en quelques caractères, sans avoir recours à une condition !

Bienvenue {{ pseudo|default:"visiteur" }}

En bref, il existe des dizaines de filtres par défaut : safe, length, etc. Tous les filtres sont répertoriés et expliqués dans la documentation officielle de Django, n'hésitez pas à y jeter un coup d'œil pour découvrir d'éventuels filtres qui pourraient vous être utiles.

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Lier template et vue Manipulons nos données avec les tags

Manipulons nos données avec les tags

Affichons nos variables à l'utilisateur Ajoutons des fichiers statiques

Manipulons nos données avec les tags

Abordons maintenant le second type d'opération implémentable dans un template : les tags. C'est grâce à ceux-ci que les conditions, boucles, etc. sont disponibles.

Les conditions : {% if %}

Tout comme en Python, il est possible d'exécuter des conditions dans votre template selon la valeur des variables passées au template :

Bonjour
{% if sexe == "Femme" %}
   Madame
{% else %}
   Monsieur
{% endif %} !

Ici, en fonction du contenu de la variable sexe, l'utilisateur ne verra pas le même texte à l'écran. Ce template est similaire au code HTML généré par la vue suivante :

def tpl(request):
    sexe = "Femme"
    html   = "Bonjour "
    if sexe == "Femme":
        html += "Madame"
    else:
        html += "Monsieur"
    html += " !"
    return HttpResponse(html)

La séparation entre vue et template simplifie grandement les choses, et permet une plus grande lisibilité que lorsque le code HTML est écrit directement dans la vue !

Il est également possible d'utiliser les structures if, elif, else de la même façon :

{% if age > 25 %}
    Bienvenue Monsieur, passez un excellent moment dans nos locaux.
{% elif age > 16 %}
    Vas-y, tu peux passer.
{% else %}
    Tu ne peux pas rentrer petit, tu es trop jeune !
{% endif %}
Les boucles : {% for %}

Tout comme les conditions, le moteur de templates de Django permet l'utilisation de la boucle for, similaire à celle de Python. Admettons que nous possédions dans notre vue un tableau de couleurs définies en Python :

couleurs = ['rouge', 'orange', 'jaune', 'vert', 'bleu', 'indigo', 'violet']

Nous décidons dès lors d'afficher cette liste dans notre template grâce à la syntaxe {% for %} suivante :

Les couleurs de l'arc-en-ciel sont :
<ul>
{% for couleur in couleurs %}
    <li>{{ couleur }}</li>
{% endfor %}
</ul>

Avec ce template, le moteur va itérer la liste (cela fonctionne avec n'importe quel autre type itérable), remplacer la variable couleur par l'élément actuel de l'itération et générer le code compris entre {% for %} et {% endfor %} pour chaque élément de la liste. Comme résultat, nous obtenons le code HTML suivant :

Les couleurs de l'arc-en-ciel sont :
<ul>
    <li>rouge</li>
    <li>orange</li>
    <li>jaune</li>
    <li>vert</li>
    <li>bleu</li>
    <li>indigo</li>
    <li>violet</li>
</ul>

Il est aussi possible de parcourir un dictionnaire, en passant par la directive {% for cle, valeur in dictionnaire.items %} :

couleurs = {'FF0000':'rouge', 
            'ED7F10':'orange', 
            'FFFF00':'jaune', 
            '00FF00':'vert', 
            '0000FF':'bleu', 
            '4B0082':'indigo', 
            '660099':'violet'}
Les couleurs de l'arc-en-ciel sont :
<ul>
{% for code, nom in couleurs.items %}
    <li style="color:#{{ code }}">{{ nom }}</li>
{% endfor %}
</ul>

Résultat :
<ul>
    <li style="color:#ED7F10">orange</li>
    <li style="color:#4B0082">indigo</li>
    <li style="color:#0000FF">bleu</li>
    <li style="color:#FFFF00">jaune</li>
    <li style="color:#660099">violet</li>
    <li style="color:#FF0000">rouge</li>
    <li style="color:#00FF00">vert</li>
</ul>

Vous pouvez aussi réaliser n'importe quelle opération classique avec la variable générée par la boucle for (ici couleur) : une condition, utiliser une autre boucle, l'afficher, etc.

Enfin, il existe une troisième directive qui peut être associée au {% for %}, il s'agit de {% empty %}. Elle permet d'afficher un message par défaut si la liste parcourue est vide. Par exemple :

<h3>Commentaires de l'article</h3>
{% for commentaire in commentaires %}
    <p>{{ commentaire }}</p>
{% empty %}
    <p class="empty">Pas de commentaires pour le moment.</p>
{% endfor %}

Ici, s'il y a au moins un élément dans commentaires, alors une suite de paragraphes sera affichée, contenant chacun un élément de la liste. Sinon, le paragraphe « Pas de commentaires pour le moment. » sera renvoyé à l'utilisateur.

Le tag {% block %}

Sur la quasi-totalité des sites web, une page est toujours composée de la même façon : un haut de page, un menu et un pied de page. Si vous copiez-collez le code de vos menus dans tous vos templates et qu'un jour vous souhaitez modifier un élément de votre menu, il vous faudra modifier tous vos templates ! Heureusement, le tag {% block %} nous permet d'éviter cette épineuse situation. En effet, il est possible de déclarer des blocs, qui seront définis dans un autre template, et réutilisables dans le template actuel. Dès lors, nous pouvons créer un fichier, appelé usuellement base.html, qui va définir la structure globale de la page, autrement dit son squelette. Par exemple:

<!DOCTYPE html>
<html lang="fr">
<head>
   <link rel="stylesheet" href="/media/css/style.css" />
   <title>{% block title %}Mon blog sur les crêpes bretonnes{% endblock %}</title>
</head>

<body>

<header>Crêpes bretonnes</header>
    <nav>
       {% block nav %}
       <ul>
           <li><a href="/">Accueil</a></li>
           <li><a href="/blog/">Blog</a></li>

      <li><a href="/contact/">Contact</a></li>
       </ul>
       {% endblock %}
   </nav>

   <section id="content">
       {% block content %}{% endblock %}
   </section>

<footer>© Crêpes bretonnes</footer>
</body>
</html>

Ce template est composé de plusieurs éléments {% block %} :

Tous ces blocs pourront être redéfinis ou inclus tels quels dans un autre template. Voyons d'ailleurs comment redéfinir et inclure ces blocs. Ayant été écrits dans le fichier base.html, nous appelons ce fichier dans chacun des templates de notre blog. Pour ce faire, nous utilisons le tag {% extends %} (pour ceux qui ont déjà fait de la programmation objet, cela doit vous dire quelque chose ; cette méthode peut aussi être assimilée à include en PHP). Nous parlons alors d'héritage de templates. Nous prenons la base que nous surchargeons, afin d'obtenir un résultat dérivé :

{% extends "base.html" %}

{% block title %}Ma page d'accueil{% endblock %}

{% block content %}
    <h2>Bienvenue !</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec rhoncus massa non tortor. 
       Vestibulum diam diam, posuere in viverra in, ullamcorper et libero. 
       Donec eget libero quis risus congue imperdiet ac id lectus.
       Nam euismod cursus arcu, et consequat libero ullamcorper sit amet.
       Sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer  
       sit amet diam. Vivamus imperdiet felis a enim tincidunt interdum.</p>
{% endblock %}

Dans cet exemple, nous avons défini deux blocs, title et content. Le tag extends va aller chercher dans le template donné en argument, ici base.html, et remplacer les blocs vides de ce dernier par les blocs de même nom définis dans le template appelé par la vue. Ainsi, title et content seront repris du template fils, mais nav sera le bloc nav défini dans base.html. En résumé, regardez la structure représentée dans l'image suivante :

Fonctionnement du tag {% block %}
Fonctionnement du tag {% block %}
Les liens vers les vues : {% url %}

Nous avons vu dans le chapitre précédent les fonctions redirect et reverse, qui respectivement redirige l'utilisateur et génère le lien vers une vue, selon certains paramètres. Une variante sous la forme de tag de la fonction reverse existe, il s'agit de {% url %}. Le fonctionnement de ce tag est très similaire à la fonction dont il est dérivé :

<a href="{% url "blog.views.view_article" 42 %}">Lien vers mon super article N° 42</a>

… générera le code HTML suivant :

<a href="/blog/article/42">Lien vers mon super article n° 42</a>

Ce code nous indique le chemin vers la vue ou son nom comme premier paramètre, entre guillemets. Les arguments qui suivent seront ceux de la vue (à condition de respecter le nombre et l'ordre des paramètres selon la déclaration de la vue bien entendu).

Nous aurions tout à fait pu utiliser une variable comme paramètre, que ce soit pour le nom de la vue ou les arguments :

<a href="{% url "blog.views.view_article" ID_article %}">Lien vers mon super article n° {{ ID_article }}</a>
Les commentaires : {% comment %}

Finalement, il existe un tag qui permet de définir des commentaires dans les templates. Ces commentaires sont différents des commentaires HTML : ils n'apparaîtront pas dans la page HTML. Cela permet par exemple de cacher temporairement une ligne, ou tout simplement de documenter votre template, afin de pouvoir mieux s'y retrouver par la suite.

Il existe deux syntaxes pour les commentaires : la première permet de faire un commentaire sur une ligne uniquement : {# Mon commentaire #}.

<p>Ma page HTML</p>
<!-- Ce commentaire HTML sera visible dans le code source. -->
{# Ce commentaire Django ne sera pas visible dans le code source. #}

Si vous souhaitez faire un commentaire sur plusieurs lignes, il vous faudra utiliser le tag {% comment %}.

{% comment %}
    Ceci est une page d'exemple. Elle est composée de 3 tableaux :
    - tableau des ventes
    - locations
    - retours en garantie
{% endcomment %}
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Affichons nos variables à l'utilisateur Ajoutons des fichiers statiques

Ajoutons des fichiers statiques

Manipulons nos données avec les tags Les modèles

Ajoutons des fichiers statiques

Pour le moment, nous n'avons utilisé que du HTML dans nos templates. Cependant, un site web est composé aujourd'hui de nombreuses ressources : CSS, JavaScript, images, etc. Nous allons donc voir comment les intégrer dans nos templates.

Tout d'abord, créons un dossier à la racine du projet, dans lequel vous enregistrerez vos fichiers. Nous l'appellerons assets. Il faut ensuite renseigner ce dossier et lui assigner une URL dans votre settings.py. Voilà les deux variables qu'il faudra modifier, ici selon notre exemple :

STATIC_URL = '/assets/'

STATICFILES_DIRS = (
    "/home/crepes/crepes_bretonnes/assets/",
)

La première variable indique l'URL du dossier depuis lequel vos fichiers seront accessibles. La deuxième renseigne le chemin vers ces fichiers sur votre disque dur.

Par la suite, si vous mettez une image, nommée par exemple salut.jpg, dans votre dossier assets (toujours selon notre exemple), vous pourrez l'inclure depuis votre template de la façon suivante :

{% load static %}
<img src="{% static 'salut.jpg' %}" alt="Mon image" />

Vous avez besoin de faire {% load static %} une fois au début de votre template, et Django s'occupe tout seul de fournir l'URL vers votre ressource. Il est déconseillé d'écrire en dur le lien complet vers les fichiers statiques, utilisez toujours {% static %}. En effet, si en production vous décidez que vos fichiers seront servis depuis l'URL assets.crepes-bretonnes.com, vous devrez modifier toutes vos URL si elles sont écrites en dur ! En revanche, si elles utilisent {% static %}, vous n'aurez qu'à éditer cette variable dans votre configuration, ce qui est tout de même bien plus pratique.

En réalité, Django ne doit pas s'occuper de servir ces fichiers, c'est à votre serveur web qu'incombe cette tâche. Cependant, en développement, étant donné que nous utilisons le serveur intégré fourni par défaut par Django, il est tout de même possible de s'arranger pour que le framework serve ces fichiers. Pour ce faire, il faut ajouter un routage spécifique à votre urls.py principal :

from django.conf.urls import patterns, include, url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns

urlpatterns = patterns('',
   # Ici vos règles classiques, comme vu au chapitre précédent
)

urlpatterns += staticfiles_urlpatterns()

Cette fonction va se baser sur les variables de votre fichier settings.py (URL et emplacement des fichiers) pour générer une règle de routage correcte et adaptée. Pour le déploiement des fichiers statiques en production, référez-vous à l'annexe consacrée à ce sujet.

En résumé
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Manipulons nos données avec les tags Les modèles

Les modèles

Ajoutons des fichiers statiques Créer un modèle

Nous avons vu comment créer des vues et des templates. Cependant, ces derniers sont presque inutiles sans les modèles, car votre site n'aurait rien de dynamique. Autant créer des pages HTML statiques !

Dans ce chapitre, nous verrons les modèles qui, comme expliqué dans la première partie, sont des interfaces permettant plus simplement d'accéder à des données dans une base de données et de les mettre à jour.

Créer un modèle

Les modèles Jouons avec des données

Créer un modèle

Un modèle s'écrit sous la forme d'une classe et représente une table dans la base de données, dont les attributs correspondent aux champs de la table. Ceux-ci se rédigent dans le fichier models.py de chaque application. Il est important d'organiser correctement vos modèles pour que chacun ait sa place dans son application, et ne pas mélanger tous les modèles dans le même models.py. Pensez à la réutilisation et à la structure du code !

Tout modèle Django se doit d'hériter de la classe mère Model incluse dans django.db.models (sinon il ne sera pas pris en compte par le framework). Par défaut, le fichier models.py généré automatiquement importe le module models de django.db. Voici un simple exemple de modèle représentant un article de blog :

#-*- coding: utf-8 -*-
from django.db import models

class Article(models.Model):
    titre = models.CharField(max_length=100)
    auteur = models.CharField(max_length=42)
    contenu = models.TextField(null=True)
    date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de parution")

    def __unicode__(self):
        """ 
        Cette méthode que nous définirons dans tous les modèles
        nous permettra de reconnaître facilement les différents objets que nous
        traiterons plus tard et dans l'administration
        """
	return u"%s" % self.titre

Pour que Django puisse créer une table dans la base de données, il faut lui préciser le type des champs qu'il doit créer. Pour ce faire, le framework propose une liste de champs qu'il sera ensuite capable de retranscrire en langage SQL. Ces derniers sont également situés dans le module models.

Dans l'exemple précédent, nous avons créé quatre attributs avec trois types de champs différents. Un CharField (littéralement, un champ de caractères) a été assigné à titre et auteur. Ce champ permet d'enregistrer une chaîne de caractères, dont la longueur maximale a été spécifiée via le paramètre max_length. Dans le premier cas, la chaîne de caractères pourra être composée de 100 caractères maximum.

Le deuxième type de champ, TextField, permet lui aussi d'enregistrer des caractères, un peu comme CharField. En réalité, Django va utiliser un autre type de champ qui ne fixe pas de taille maximale à la chaîne de caractères, ce qui est très pratique pour enregistrer de longs textes.

Finalement, le champ DateTimeField prend comme valeur un objet DateTime du module datetime de la bibliothèque standard. Il est donc possible d'enregistrer autre chose que du texte !

Insistons ici sur le fait que les champs du modèle peuvent prendre plusieurs arguments. Certains sont spécifiques au champ, d'autres non. Par exemple, le champ DateTimeField possède un argument facultatif : auto_now_add. S'il est mis à True, lors de la création d'une nouvelle entrée, Django mettra automatiquement à jour la valeur avec la date et l'heure de la création de l'objet. Un autre argument du même genre existe, auto_now, qui permet à peu près la même chose, mais fera en sorte que la date soit mise à jour à chaque modification de l'entrée.

L'argument verbose_name en revanche est un argument commun à tous les champs de Django. Il peut être passé à un DateTimeField, CharField, TextField, etc. Il sera notamment utilisé dans l'administration générée automatiquement pour donner une précision quant au nom du champ. Ici, nous avons insisté sur le fait que la date correspond bien à la date de parution de l'article. Le paramètre null, lorsque mis à True, indique à Django que ce champ peut être laissé vide et qu'il est donc optionnel.

Il existe beaucoup d'autres champs disponibles, ceux-ci sont repris dans la documentation de Django. N'hésitez pas à la consulter en cas de doute ou question !

Pour que Django crée la table associée au modèle, il suffit de lancer la commande syncdb via manage.py :

python manage.py syncdb

Étant donné que c'est la première fois que vous lancez la commande, Django va créer d'autres tables plus générales (utilisateurs, groupes, sessions, etc.), comme à la figure suivante. À un moment, Django vous proposera de créer un compte administrateur. Répondez par yes et complétez les champs qu'il proposera par la suite. Nous reviendrons sur tout cela plus tard.

Aperçu des tables créées dans un outil de gestion de base de données
Aperçu des tables créées dans un outil de gestion de base de données

La table associée au modèle Article étant créée, nous pouvons commencer à jouer avec !

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les modèles Jouons avec des données

Jouons avec des données

Créer un modèle Les liaisons entre modèles

Jouons avec des données

Django propose un interpréteur interactif Python synchronisé avec votre configuration du framework. Il est possible via celui-ci de manipuler nos modèles comme si nous étions dans une vue. Pour ce faire, il suffit d'utiliser une autre commande de l'utilitaire manage.py :

$ python manage.py shell
Python 2.7.3 (default, Apr 24 2012, 00:00:54) 
[GCC 4.7.0 20120414 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

Commençons par importer le modèle que nous avons justement créé :

>>> from blog.models import Article

Pour ajouter une entrée dans la base de données, il suffit de créer une nouvelle instance de la classe Article et de la sauvegarder. Chaque instance d'un modèle correspond donc à une entrée dans la base de données. Nous pouvons spécifier la valeur des attributs directement pendant l'instanciation de classe, ou l'assigner par la suite :

>>> article = Article(titre="Bonjour", auteur="Maxime")
>>> article.contenu = u"Les crêpes bretonnes sont trop bonnes !"

Pourquoi n'avons-nous pas mis de valeur à l'attribut date du modèle ?

date est un DateTimeField dont le paramètre auto_now_add a été mis à True. Dès lors, Django se charge tout seul de le mettre à jour avec la bonne date et heure lors de la création. Cependant, il est tout de même obligatoire de remplir tous les champs pour chaque entrée sauf cas comme celui-là, sans quoi Django retournera une erreur !

Nous pouvons bien évidemment accéder aux attributs de l'objet comme pour n'importe quel autre objet Python :

>>> print article.auteur
Maxime

Pour sauvegarder l'entrée dans la base de données (les modifications ne sont pas enregistrées en temps réel), il suffit d'appeler la méthode save, de la classe mère Model dont hérite chaque modèle :

>>> article.save()

L'entrée a été créée et enregistrée !

Bien évidemment, il est toujours possible de modifier l'objet par la suite :

>>> article.titre = "Salut !"
>>> article.auteur = "Mathieu"
>>> article.save()

Il ne faut cependant pas oublier d'appeler la méthode save à chaque modification, sinon les changements ne seront pas sauvegardés.

Pour supprimer une entrée dans la base de données, rien de plus simple, il suffit d'appeler la méthode delete d'un objet :

>>> article.delete()

Nous avons vu comment créer, éditer et supprimer des entrées. Il serait pourtant également intéressant de pouvoir les obtenir par la suite, pour les afficher par exemple. Pour ce faire, chaque modèle (la classe, et non l'instance, attention !), possède plusieurs méthodes dans la sous-classe objects. Par exemple, pour obtenir toutes les entrées enregistrées d'un modèle, il faut appeler la méthode all() :

>>> Article.objects.all()
[]

Bien évidemment, étant donné que nous avons supprimé l'article créé un peu plus tôt, l'ensemble renvoyé est vide, créons rapidement deux nouvelles entrées :

>>> Article(auteur="Mathieu", titre=u"Les crêpes", contenu=u"Les crêpes c'est cool").save()
>>> Article(auteur="Maxime", titre="La Bretagne", contenu="La Bretagne c'est trop bien").save()

Cela étant fait, réutilisons la méthode all :

>>> Article.objects.all()
[<Article: Les crêpes>, <Article: La Bretagne>]

L'ensemble renvoyé par la fonction n'est pas une vraie liste, mais un QuerySet. Il s'agit d'un conteneur itérable qui propose d'autres méthodes sur lesquelles nous nous attarderons par la suite. Nous avons donc deux éléments, chacun correspondant à un des articles que nous avons créés.

Nous pouvons donc par exemple afficher les différents titres de nos articles :

>>> for article in Article.objects.all():
...	print article.titre

Les crêpes
La Bretagne

Maintenant, imaginons que vous souhaitiez sélectionner tous les articles d'un seul auteur uniquement. La méthode filter a été conçue dans ce but. Elle prend en paramètre une valeur d'un ou plusieurs attributs et va passer en revue toutes les entrées de la table et ne sélectionner que les instances qui ont également la valeur de l'attribut correspondant. Par exemple :

>>> for article in Article.objects.filter(auteur="Maxime"):
...	print article.titre, "par", article.auteur

La Bretagne par Maxime

Efficace ! L'autre article n'a pas été repris dans le QuerySet, car son auteur n'était pas Maxime mais Mathieu.

Une méthode similaire à filter existe, mais fait le contraire : exclude. Comme son nom l'indique, elle exclut les entrées dont la valeur des attributs passés en arguments coïncide :

>>> for article in Article.objects.exclude(auteur="Maxime"):
...	print article.titre, "par", article.auteur

Les crêpes par Mathieu

Sachez que vous pouvez également filtrer ou exclure des entrées à partir de plusieurs champs : Article.objects.filter(titre="Coucou", auteur="Mathieu") renverra un QuerySet vide, car il n'existe aucun article de Mathieu intitulé « Coucou ».

Il est même possible d'aller plus loin, en filtrant par exemple les articles dont le titre doit contenir certains caractères (et non pas être strictement égal à une chaîne entière). Si nous souhaitons prendre tous les articles dont le titre comporte le mot « crêpe », il faut procéder ainsi :

>>> Article.objects.filter(titre__contains="crêpe")
[<Article: Les crêpes>]

Ces méthodes de recherche spéciales sont construites en prenant le champ concerné (ici titre), auquel nous ajoutons deux underscores « __ », suivis finalement de la méthode souhaitée. Ici, il s'agit donc de titre__contains, qui veut dire littéralement « prends tous les éléments dont le titre contient le mot passé en argument ».

D'autres méthodes du genre existent, notamment la possibilité de prendre des valeurs du champ (strictement) inférieures ou (strictement) supérieures à l'argument passé, grâce à la méthode lt (less than, plus petit que) et gt (greater than, plus grand que) :

>>> from datetime import datetime
>>> Article.objects.filter(date__lt=datetime.now())
[<Article: Les crêpes>, <Article: La Bretagne>]

Les deux articles ont été sélectionnés, car ils remplissent tous deux la condition (leur date de parution est inférieure au moment actuel). Si nous avions utilisé gt au lieu de lt, la requête aurait renvoyé un QuerySet vide, car aucun article n'a été publié après le moment actuel.

De même, il existe lte et gte qui opèrent de la même façon, la différence réside juste dans le fait que ceux-ci prendront tout élément inférieur/supérieur ou égal (lte : less than or equal, plus petit ou égal, idem pour gte).

Sur la page d'accueil de notre blog, nous souhaiterons organiser les articles par date de parution, du plus récent au plus ancien. Pour ce faire, il faut utiliser la méthode order_by. Cette dernière prend comme argument une liste de chaînes de caractères qui correspondent aux attributs du modèle :

>>> Article.objects.order_by('date')
[<Article: Les crêpes>, <Article: La Bretagne>]

Le tri se fait par ordre ascendant (ici du plus ancien au plus récent, nous avons enregistré l'article sur les crêpes avant celui sur la Bretagne). Pour spécifier un ordre descendant, il suffit de précéder le nom de l'attribut par le caractère « - » :

>>> Article.objects.order_by('-date')
[<Article: La Bretagne>, <Article: Les crêpes>]

Il est possible de passer plusieurs noms d'attributs à order_by. La priorité de chaque attribut dans le tri est déterminée par sa position dans la liste d'arguments. Ainsi, si nous trions les articles par nom et que deux d'entre eux ont le même nom, Django les départagera selon le deuxième attribut, et ainsi de suite tant que des attributs comparés seront identiques.

Accessoirement, nous pouvons inverser les éléments d'un QuerySet en utilisant la méthode reverse().

Finalement, dernière caractéristique importante des méthodes de QuerySet, elles sont cumulables, ce qui garantit une grande souplesse dans vos requêtes :

>>> Article.objects.filter(date__lt=datetime.now()).order_by('date','titre').reverse()
[<Article: La Bretagne>, <Article: Les crêpes>]

Pour terminer cette (longue) section, nous allons introduire des méthodes qui, contrairement aux précédentes, retournent un seul objet et non un QuerySet.

Premièrement, get, comme son nom l'indique, permet d'obtenir une et une seule entrée d'un modèle. Il prend les mêmes arguments que filter ou exclude. S'il ne retrouve aucun élément correspondant aux conditions, ou plus d'un seul, il retourne une erreur :

>>> Article.objects.get(titre="Je n'existe pas")

...
DoesNotExist: Article matching query does not exist. Lookup parameters were {'titre': "Je n'existe pas"}
>>> print Article.objects.get(auteur="Mathieu").titre
Les crêpes
>>> Article.objects.get(titre__contains="L")
...
MultipleObjectsReturned: get() returned more than one Article -- it returned 2! Lookup parameters were {'titre__contains': 'L'}

Dans le même style, il existe une méthode permettant de créer une entrée si aucune autre n'existe avec les conditions spécifiées. Il s'agit de get_or_create. Cette dernière va renvoyer un tuple contenant l'objet désiré et un booléen qui indique si une nouvelle entrée a été créée ou non :

Article.objects.get_or_create(auteur="Mathieu")
>>> (<Article: Les crêpes>, False)

Article.objects.get_or_create(auteur="Zozor", titre="Hi han")
>>> (<Article: Hi han>, True)
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Créer un modèle Les liaisons entre modèles

Les liaisons entre modèles

Jouons avec des données Les modèles dans les vues

Les liaisons entre modèles

Il est souvent pratique de lier deux modèles entre eux, pour relier un article à une catégorie par exemple. Django propose tout un système permettant de simplifier grandement les différents types de liaison. Nous traiterons ce sujet dans ce sous-chapitre.

Reprenons notre exemple des catégories et des articles. Lorsque vous concevrez votre base de données, vous allez souvent faire des liens entre les classes (qui représentent nos tables SQL dans notre site), comme à la figure suivante.

Ici, un article peut être lié à une et une seule catégorie, et une catégorie peut être attributée à une infinité d'articles
Ici, un article peut être lié à une et une seule catégorie, et une catégorie peut être attributée à une infinité d'articles

Pour traduire cette relation, nous allons d'abord devoir créer un autre modèle représentant les catégories. Ce dernier est relativement simple :

class Categorie(models.Model):
    nom = models.CharField(max_length=30)

    def __unicode__(self):
        return self.nom

Maintenant, créons la liaison depuis notre modèle Article, qu'il va falloir modifier en lui ajoutant un nouveau champ :

class Article(models.Model):
    titre = models.CharField(max_length=100)
    auteur = models.CharField(max_length=42)
    contenu = models.TextField(null=True)
    date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de parution")
    categorie = models.ForeignKey('Categorie')

    def __unicode__(self):
        return self.titre

Nous avons donc ajouté un champ ForeignKey. En français, ce terme est traduit par « clé étrangère ». Il va enregistrer une clé, un identifiant propre à chaque catégorie enregistrée (il s'agit la plupart du temps d'un nombre), qui permettra donc de retrouver la catégorie associée.

Nous avons modifié notre classe Article et allons rencontrer un des rares défauts de Django. En effet, si nous lancions maintenant manage.py syncdb, il créerait bien la table correspondant au modèle Categorie, mais n'ajouterait pas le champ ForeignKey dans la table Article pour autant. Pour résoudre ce problème, deux méthodes s'offrent à vous :

La base de données étant prête, ouvrez à nouveau un shell via manage.py shell. Importons les modèles et créons une nouvelle catégorie :

>>> from blog.models import Categorie, Article

>>> cat = Categorie(nom=u"Crêpes")
>>> cat.save()

>>> art = Article()
>>> art.titre=u"Les nouvelles crêpes"
>>> art.auteur="Maxime"
>>> art.contenu=u"On a fait de nouvelles crêpes avec du trop bon rhum"
>>> art.categorie = cat
>>> art.save()

Pour accéder aux attributs et méthodes de la catégorie associée à l'article, rien de plus simple :

>>> print art.categorie.nom
Crêpes

Dans cet exemple, si un article ne peut avoir qu'une seule catégorie, une catégorie peut en revanche avoir plusieurs articles. Pour réaliser l'opération en sens inverse (accéder aux articles d'une catégorie depuis cette dernière), une sous-classe s'est créée toute seule avec la ForeignKey :

>>> cat.article_set.all()
[<Article: Les nouvelles crêpes>]

Le nom que prendra une relation en sens inverse est composé du nom du modèle source (qui a la ForeignKey comme attribut), d'un seul underscore « _ » et finalement du mot set qui signifie en anglais « ensemble ». Nous accédons donc ici à l'ensemble des articles d'une catégorie. Cette relation opère exactement comme n'importe quelle sous-classe objects d'un modèle, et renvoie ici tous les articles de la catégorie. Nous pouvons utiliser les méthodes que nous avons vues précédemment : all, filter, exclude, order_by…

Point important : il est possible d'accéder aux attributs du modèle lié par une clé étrangère depuis un filter, exclude, order_by… Nous pourrions ici par exemple filtrer tous les articles dont le titre de la catégorie possède un certain mot :

>>> Article.objects.filter(categorie__nom__contains=u"crêpes")
[<Article: Les nouvelles crêpes>]

Accéder à un élément d'une clé étrangère se fait en ajoutant deux underscores « __ », comme avec les méthodes de recherche spécifiques, suivis du nom de l'attribut recherché. Comme montré dans l'exemple, nous pouvons encore ajouter une méthode spéciale de recherche sans aucun problème !

Un autre type de liaison existe, très similaire au principe des clés étrangères : le OneToOneField. Ce dernier permet de lier un modèle à un autre tout aussi facilement, et garantit qu'une fois la liaison effectuée plus aucun autre objet ne pourra être associé à ceux déjà associés. La relation devient unique. Si nous avions utilisé notre exemple avec un OneToOneField, chaque catégorie ne pourrait avoir qu'un seul article associé, et de même pour chaque article.

Un autre bref exemple :

class Moteur(models.Model):
    nom = models.CharField(max_length=25)

    def __unicode__(self):
        return self.nom

class Voiture(models.Model):
    nom = models.CharField(max_length=25)
    moteur = models.OneToOneField(Moteur)

    def __unicode__(self):
        return self.nom

N'oubliez pas de faire un manage.py syncdb !

Nous avons deux objets, un moteur nommé « Vroum » et une voiture nommée « Crêpes-mobile » qui est liée au moteur. Nous pouvons accéder du moteur à la voiture ainsi, depuis manage.py shell :

>>> from blog.models import Moteur, Voiture
>>> moteur = Moteur.objects.create(nom="Vroum") # create crée directement l'objet et l'enregistre
>>> voiture = Voiture.objects.create(nom=u"Crêpes-mobile", moteur=moteur)

>>> moteur.voiture
<Voiture: Crêpes-mobile>
>>> voiture.moteur
<Moteur: Vroum>

Ici, le OneToOneField a créé une relation en sens inverse qui ne va plus renvoyer un QuerySet, mais directement l'élément concerné (ce qui est logique, celui-ci étant unique). Cette relation inverse prendra simplement le nom du modèle, qui n'est donc plus suivi par _set.

Sachez qu'il est possible de changer le nom de la variable créée par la relation inverse (précédemment article_set et moteur). Pour ce faire, il faut utiliser l'argument related_name du ForeignKey ou OneToOneField et lui passer une chaîne de caractères désignant le nouveau nom de la variable (à condition que cette chaîne représente bien un nom de variable valide !). Cette solution est notamment utilisée en cas de conflit entre noms de variables. Accessoirement, il est même possible de désactiver la relation inverse en donnant related_name='+'.

Finalement, dernier type de liaison, le plus complexe : le ManyToManyField (traduit littéralement, « plusieurs-à-plusieurs »). Reprenons un autre exemple simple : nous construisons un comparateur de prix pour les ingrédients nécessaires à la réalisation de crêpes. Plusieurs vendeurs proposent plusieurs produits, parfois identiques, à des prix différents.

Il nous faudra trois modèles :

class Produit(models.Model):
    nom = models.CharField(max_length=30)

    def __unicode__(self):
        return self.nom

class Vendeur(models.Model):
    nom = models.CharField(max_length=30)
    produits = models.ManyToManyField(Produit, through='Offre')
	
    def __unicode__(self):
        return self.nom

class Offre(models.Model):
    prix = models.IntegerField()
    produit = models.ForeignKey(Produit)
    vendeur = models.ForeignKey(Vendeur)

    def __unicode__(self):
        return "{0} vendu par {1}".format(self.produit, self.vendeur)

Explications ! Les modèles Produit et Vendeur sont classiques, à l'exception du fait que nous avons utilisé un ManyToManyField dans Vendeur, au lieu d'une ForeignKey ou de OneToOneField comme précédemment. La nouveauté, en revanche, est bien le troisième modèle : Offre. C'est celui-ci qui fait le lien entre Produit et Vendeur et permet d'ajouter des informations supplémentaires sur la liaison (ici le prix, caractérisé par un IntegerField qui enregistre un nombre).

Un ManyToManyField va toujours créer une table intermédiaire qui enregistrera les clés étrangères des différents objets des modèles associés. Nous pouvons soit laisser Django s'en occuper tout seul, soit la créer nous-mêmes pour y ajouter des attributs supplémentaires (pour rappel, ici nous ajoutons le prix). Dans ce deuxième cas, il faut spécifier le modèle faisant la liaison via l'argument through du ManyToManyField et ne surtout pas oublier d'ajouter des ForeignKey vers les deux modèles qui seront liés.

Créez les tables via syncdb et lancez un shell. Enregistrons un vendeur et deux produits :

>>> from blog.models import Vendeur, Produit, Offre
>>> vendeur = Vendeur.objects.create(nom="Carouf")
>>> p1 = Produit.objects.create(nom="Lait") 
>>> p2 = Produit.objects.create(nom="Farine")

Désormais, la gestion du ManyToMany se fait de deux manières différentes. Soit nous spécifions manuellement la table intermédiaire, soit nous laissons Django le faire. Étant donné que nous avons opté pour la première méthode, tout ce qu'il reste à faire, c'est créer un nouvel objet Offre qui reprend le vendeur, le produit et son prix :

>>> o1 = Offre.objects.create(vendeur=vendeur, produit=p1, prix=10)
>>> o2 = Offre.objects.create(vendeur=vendeur, produit=p2, prix=42)

Si nous avions laissé Django générer automatiquement la table, il aurait fallu procéder ainsi :

vendeur.produits.add(p1,p2)

Pour supprimer une liaison entre deux objets, deux méthodes se présentent encore. Avec une table intermédiaire spécifiée manuellement, il suffit de supprimer l'objet faisant la liaison (supprimer un objet Offre ici), autrement nous utilisons une autre méthode du ManyToManyField :

vendeur.produits.remove(p1) # Nous avons supprimé p1, il ne reste plus que p2 qui est lié au vendeur

Ensuite, pour accéder aux objets du modèle source (possédant la déclaration du ManyToManyField, ici Vendeur) associés au modèle destinataire (ici Produit), rien de plus simple, nous obtenons à nouveau un QuerySet :

>>> vendeur.produits.all()
[<Produit: Lait>, <Produit: Farine>]

Encore une fois, toutes les méthodes des QuerySet (filter, exclude, order_by, reverse…) sont également accessibles.

Comme pour les ForeignKey, une relation inverse s'est créée :

>>> p1.vendeur_set.all()
[<Vendeur: Carouf>]

Pour rappel, il est également possible avec des ManyToMany de modifier le nom de la variable faisant la relation inverse via l'argument related_name.

Accessoirement, si nous souhaitons accéder aux valeurs du modèle intermédiaire (ici Offre), il faut procéder de manière classique :

>>> Offre.objects.get(vendeur=vendeur, produit=p1).prix
10

Finalement, pour supprimer toutes les liaisons d'un ManyToManyField, que la table intermédiaire soit générée automatiquement ou manuellement, nous pouvons appeler la méthode clear :

>>> vendeur.produits.clear()
>>> vendeur.produits.all()
[]

Et tout a disparu !

Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Jouons avec des données Les modèles dans les vues

Les modèles dans les vues

Les liaisons entre modèles L'administration

Les modèles dans les vues

Nous avons vu comment utiliser les modèles dans la console, et d'une manière plutôt théorique. Nous allons ici introduire les modèles dans un autre milieu plus utile : les vues.

Afficher les articles du blog

Pour afficher les articles de notre blog, il suffit de reprendre une de nos requêtes précédentes, et l'incorporer dans une vue. Dans notre template, nous ajouterons un lien vers notre article pour pouvoir le lire en entier. Le problème qui se pose ici, et que nous n'avons pas soulevé avant, est le choix d'un identifiant. En effet, comment passer dans l'URL une information facile à transcrire pour désigner un article particulier ?

En réalité, nos modèles contiennent plus d'attributs et de champs SQL que nous en déclarons. Nous pouvons le remarquer depuis la commande python manage.py sql blog, qui renvoie la structure SQL des tables créées :

BEGIN;
CREATE TABLE "blog_categorie" (
    "id" integer NOT NULL PRIMARY KEY,
    "nom" varchar(30) NOT NULL
)
;
CREATE TABLE "blog_article" (
    "id" integer NOT NULL PRIMARY KEY,
    "titre" varchar(100) NOT NULL,
    "auteur" varchar(42) NOT NULL,
    "contenu" text NOT NULL,
    "date" datetime NOT NULL,
    "categorie_id" integer NOT NULL REFERENCES "blog_categorie" ("id")
)
;
COMMIT;

Note : nous n'avons sélectionné ici que les modèles Categorie et Article.

Chaque table contient les attributs définis dans le modèle, mais également un champ id qui est un nombre auto-incrémenté (le premier article aura l'ID 1, le deuxième l'ID 2, etc.), et donc unique ! C'est ce champ qui sera utilisé pour désigner un article particulier. Passons à quelque chose de plus concret, voici un exemple d'application :

from django.http import Http404
from django.shortcuts import render
from blog.models import Article

def accueil(request):
    """ Afficher tous les articles de notre blog """
    articles = Article.objects.all() # Nous sélectionnons tous nos articles
    return render(request, 'blog/accueil.html', {'derniers_articles':articles})

def lire(request, id):
    """ Afficher un article complet """
    pass # Le code de cette fonction est donné un peu plus loin.
urlpatterns = patterns('blog.views',
    url(r'^$', 'accueil'),
    url(r'^article/(?P<id>\d+)$', 'lire'),
)
<h1>Bienvenue sur le blog des crêpes bretonnes !</h1>

{% for article in derniers_articles %}
    <div class="article">
    	<h3>{{ article.titre }}</h3>
    	<p>{{ article.contenu|truncatewords_html:80 }}</p>
        <p><a href="{% url "blog.views.lire" article.id %}">Lire la suite</a>
    </div>
{% empty %}
    <p>Aucun article.</p>
{% endfor %}

Nous récupérons tous les articles via la méthode objects.all() et nous renvoyons la liste au template. Dans le template, il n'y a rien de fondamentalement nouveau non plus : nous affichons un à un les articles. Le seul point nouveau est celui que nous avons cité précédemment : nous faisons un lien vers l'article complet, en jouant avec le champ id de la table SQL. Si vous avez correctement suivi le sous-chapitre sur les manipulations d'entrées et tapé nos commandes, vous devriez avoir un article enregistré.

Afficher un article précis

L'affichage d'un article précis est plus délicat : il faut vérifier que l'article demandé existe, et renvoyer une erreur 404 si ce n'est pas le cas. Notons déjà qu'il n'y a pas besoin de vérifier si l'ID précisé est bel et bien un nombre, cela est déjà spécifié dans urls.py.

Une vue possible est la suivante :

def lire(request, id):
    try:
        article = Article.objects.get(id=id)
    except Article.DoesNotExist:
        raise Http404

    return render(request, 'blog/lire.html', {'article':article})

C'est assez verbeux, or les développeurs Django sont très friands de raccourcis. Un raccourci particulièrement utile ici est get_object_or_404, permettant de récupérer un objet selon certaines conditions, ou renvoyer la page d'erreur 404 si aucun objet n'a été trouvé. Le même raccourci existe pour obtenir une liste d'objets : get_list_or_404.

# Il faut ajouter l'import get_object_or_404, attention !
from django.shortcuts import render, get_object_or_404

def lire(request, id):
    article = get_object_or_404(Article, id=id)
    return render(request, 'blog/lire.html', {'article':article})

Voici le template lire.html associé à la vue :

<h1>{{ article.titre }} <span class="small">dans {{ article.categorie.nom }}</span></h1>
<p class="infos">Rédigé par {{ article.auteur }}, le {{ article.date|date:"DATE_FORMAT" }}</p>
<div class="contenu">{{ article.contenu|linebreaks }}</div>

Ce qui nous donne la figure suivante.

À cette adresse, la vue de notre article (l'ID à la fin est variable, attention) : http://127.0.0.1:8000/blog/article/2
À cette adresse, la vue de notre article (l'ID à la fin est variable, attention) : http://127.0.0.1:8000/blog/article/2
Des URL plus esthétiques

Comme vous pouvez le voir, nos URL contiennent pour le moment un ID permettant de déterminer quel article il faut afficher. C'est relativement pratique, mais cela a l'inconvénient de ne pas être très parlant pour l'utilisateur. Pour remédier à cela, nous voyons de plus en plus fleurir sur le web des adresses contenant le titre de l'article réécrit. Par exemple, le Site du Zéro emploie cette technique à plusieurs endroits, comme avec l'adresse de ce cours : http://www.siteduzero.com/informatique/tutoriels/creez-vos-applications-web-avec-django-1. Nous pouvons y identifier la chaîne « creez-vos-applications-web-avec-django » qui nous permet de savoir de quoi parle le lien, sans même avoir cliqué dessus. Cette chaîne est couramment appelée un slug. Et pour définir ce terme barbare, rien de mieux que Wikipédia :

Citation : Wikipédia - Slug (journalisme)

Un slug est en journalisme un label court donné à un article publié, ou en cours d'écriture. Il permet d'identifier l'article tout au long de sa production et dans les archives. Il peut contenir des informations sur l'état de l'article, afin de les catégoriser.

Nous allons intégrer la même chose à notre système de blog. Pour cela, il existe un type de champ un peu spécial dans les modèles : le SlugField. Il permet de stocker une chaîne de caractères, d'une certaine taille maximale. Ainsi, notre modèle devient le suivant :

class Article(models.Model):
    titre = models.CharField(max_length=100)
    slug = models.SlugField(max_length=100)
    auteur = models.CharField(max_length=42)
    contenu = models.TextField(null=True)
    date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de parution")

    def __unicode__(self):
	return self.titre

N'oubliez pas de mettre à jour la structure de votre table, comme nous l'avons déjà expliqué précédemment, et de créer une nouvelle entrée à partir de manage.py shell !

Désormais, nous pouvons aisément ajouter notre slug dans l'URL, en plus de l'ID lors de la construction d'une URL. Nous pouvons par exemple utiliser des URL comme celle-ci : /blog/article/1-titre-de-l-article. La mise en œuvre est également rapide à mettre en place :

urlpatterns = patterns('blog.views',
    url(r'^/$', 'accueil'),
    url(r'^article/(?P<id>\d+)-(?P<slug>.+)$', 'lire'),
)
from django.shortcuts import render, get_object_or_404

def lire(request, id, slug):
    article = get_object_or_404(Article, id=id, slug=slug)
    return render(request, 'blog/lire.html', {'article':article})
<p><a href="{% url "blog.views.lire" article.id article.slug %}">Lire la suite</a>

L'inconvénient ici est qu'il faut renseigner pour le moment le slug à la main à la création d'un article. Nous verrons au chapitre suivant qu'il est possible d'automatiser son remplissage.

En résumé
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Les liaisons entre modèles L'administration

L'administration

Les modèles dans les vues Mise en place de l'administration

Sur un bon nombre de sites, l'interface d'administration est un élément capital à ne pas négliger lors du développement. C'est cette partie qui permet en effet de gérer les diverses informations disponibles : les articles d'un blog, les comptes utilisateurs, etc.
Un des gros points forts de Django est que celui-ci génère de façon automatique l'administration en fonction de vos modèles. Celle-ci est personnalisable à souhait en quelques lignes et est très puissante.

Nous verrons dans ce chapitre comment déployer l'administration et la personnaliser.

Mise en place de l'administration

L'administration Première prise en main

Mise en place de l'administration

Les modules django.contrib

L'administration Django est optionnelle : il est tout à fait possible de développer un site sans l'utiliser. Pour cette raison, elle est placée dans le module django.contrib, contenant un ensemble d'extensions fournies par Django, réutilisables dans n'importe quel projet. Ces modules sont bien pensés et vous permettent d'éviter de réinventer la roue à chaque fois.
Nous allons étudier ici le module django.contrib.admin qui génère l'administration. Il existe toutefois bien d'autres modules, dont certains que nous aborderons par la suite : django.contrib.messages (gestion de messages destinés aux visiteurs), django.contrib.auth (système d'authentification et de gestion des utilisateurs), etc.

Accédons à cette administration !
Import des modules

Ce module étant optionnel, il est nécessaire d'ajouter quelques lignes dans notre fichier de configuration pour pouvoir en profiter. Ouvrons donc notre fichier settings.py. Comme vu précédemment, la variable INSTALLED_APPS permet à Django de savoir quels sont les modules à charger au démarrage du serveur. Si django.contrib.admin n'apparaît pas, ajoutez-le (l'ordre dans la liste n'a pas d'importance).
L'administration nécessite toutefois quelques dépendances pour fonctionner, également fournies dans django.contrib. Ces dépendances sont :

qui sont normalement incluses de base dans INSTALLED_APPS.
Au final, vous devriez avoir une variable INSTALLED_APPS qui ressemble à ceci :

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog', #Nous avions ajouté celui-ci lors de l'ajout de l'application
    # Uncomment the next line to enable the admin:
     'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
)

Finalement, le module admin nécessite aussi l'import de middlewares, normalement inclus par défaut également :

Sauvegardez le fichier settings.py. Désormais, lors du lancement du serveur, le module contenant l'administration sera importé.

Mise à jour de la base de données

Pour fonctionner, il faut créer de nouvelles tables dans la base de données, qui serviront à enregistrer les actions des administrateurs, définir les droits de chacun, etc. Pour ce faire, il faut procéder comme avec les modèles et utiliser la commande suivante : python manage.py syncdb. À la première exécution, cette commande vous demandera de renseigner des informations pour créer un compte super-utilisateur, qui sera au début le seul compte à pouvoir accéder à l'administration. Cette opération commence notamment par la directive suivante :

You just installed Django's auth system, which means you don't have any superusers defined.

Répondez yes et insérez les informations utilisateur que Django vous demande.

Si vous sautez cette étape, il sera toujours possible de (re)créer ce compte via la commande python manage.py createsuperuser.

Intégration à notre projet : définissons-lui une adresse

Enfin, tout comme pour nos vues, il est nécessaire de dire au serveur « Quand j'appelle cette URL, redirige-moi vers l'administration. » En effet, pour l'instant nous avons bel et bien importé le module, mais nous ne pouvons pas encore y accéder.

Comme pour les vues, cela se fait à partir d'un urls.py. Ouvrez le fichier crepes_bretonnes/urls.py.
Par défaut, Django a déjà indiqué plusieurs lignes pour l'administration, mais celles-ci sont commentées. Au final, après avoir décommenté ces lignes, votre fichier urls.py devrait ressembler à ceci :

from django.conf.urls import patterns, include, url

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # D'autres éventuelles directives.
    # Uncomment the next line to enable the admin:
    url(r'^admin/', include(admin.site.urls)),
)

Nous voyons que par défaut, l'administration sera disponible à l'adresse http://localhost:8000/admin/. Une fois les lignes décommentées, lancez le serveur Django (ou relancez-le s'il est déjà lancé). Vous pouvez dès lors accéder à l'administration depuis l'URL définie (voir la figure suivante), il suffira juste de vous connecter avec le nom d'utilisateur et le mot de passe que vous avez spécifiés lors du syncdb ou createsuperuser.

L'écran de connexion de l'administration
L'écran de connexion de l'administration
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

L'administration Première prise en main

Première prise en main

Mise en place de l'administration Administrons nos propres modèles

Première prise en main

Une fois que vous avez saisi vos identifiants de super-utilisateur, vous devez arriver sur une page semblable à la figure suivante.

Accueil de l'administration
Accueil de l'administration

C'est encore un peu vide, mais ne vous inquiétez pas, nous allons bientôt pouvoir manipuler nos modèles Article et Catégorie, rédigés dans le chapitre précédent.
Tout d'abord, faisons un petit tour des fonctionnalités disponibles. Sur cette page, vous avez la liste des modèles que vous pouvez gérer. Ces modèles sont au nombre de 3 : Groupes, Utilisateurs et Sites. Ce sont les modèles par défaut.
Chaque modèle possède ensuite une interface qui permet de réaliser les 4 opérations de base « CRUD » : Create, Read, Update, Delete (littéralement créer, lire, mettre à jour, supprimer).
Pour ce faire, allons dans l'administration des comptes sur notre site, en cliquant sur Utilisateurs. Pour le moment, vous n'avez logiquement qu'un compte dans la liste, le vôtre, ainsi que vous pouvez le voir sur la figure suivante.

Liste des utilisateurs
Liste des utilisateurs

C'est à partir d'ici que nous pouvons constater la puissance de cette administration : sans avoir écrit une seule ligne de code, il est possible de manipuler la liste des utilisateurs dans tous les sens : la filtrer selon certains paramètres, la trier avec certains champs, effectuer des actions sur certaines lignes, etc.
Pour essayer ces opérations, nous allons d'abord créer un deuxième compte utilisateur. Il suffit de cliquer sur le bouton Ajouter utilisateur, disponible en haut à droite. Le premier formulaire vous demande de renseigner le nom d'utilisateur et le mot de passe. Nous pouvons déjà remarquer sur la figure suivante que les formulaires peuvent gérer des contraintes, et l'affichage d'erreurs.

Formulaire de création de comptes, après le validateur avec erreur
Formulaire de création de comptes, après le validateur avec erreur

Une fois cela validé, vous accédez directement à un formulaire plus complet, permettant de renseigner plus d'informations sur l'utilisateur qui vient d'être créé : ses informations personnelles, mais aussi ses droits sur le site.
Django fournit de base une gestion précise des droits, par groupe et par utilisateur, offrant souplesse et rapidité dans l'attribution des droits. Ainsi, ici nous pouvons voir qu'il est possible d'assigner un ou plusieurs groupes à l'utilisateur, et des permissions spécifiques. D'ailleurs, vous pouvez créer un groupe sans quitter cette fenêtre en cliquant sur le « + » vert à côté des choix (qui est vide chez vous pour le moment) !
Également, deux champs importants sont Statut équipe et Statut super-utilisateur : le premier permet de définir si l'utilisateur peut accéder au panel d'administration, et le second de donner « les pleins pouvoirs » à l'utilisateur (voir la figure suivante).

Exemple d'édition des permissions, ici j'ai créé deux groupes avant d'éditer l'utilisateur
Exemple d'édition des permissions, ici j'ai créé deux groupes avant d'éditer l'utilisateur

Une fois que vous avez fini de gérer l'utilisateur, vous êtes redirigés vers la liste de tout à l'heure, avec une ligne en plus. Désormais, vous pouvez tester le tri, et les filtres qui sont disponibles à la droite du tableau ! Nous verrons d'ailleurs plus tard comment définir les champs à afficher, quels filtres utiliser, etc.

En définitive, pour finir ce rapide tour des fonctionnalités, vous avez peut-être remarqué la présence d'un bouton Historique en haut de chaque fiche utilisateur ou groupe. Ce bouton est très pratique, puisqu'il vous permet de suivre les modifications apportées, et donc de voir rapidement l'évolution de l'objet sur le site. En effet, chaque action effectuée via l'administration est inscrite dans un journal des actions. De même, sur l'index vous avez la liste de vos dernières actions, vous permettant de voir ce que vous avez fait récemment, et d'accéder rapidement aux liens, en cas d'erreur par exemple (voir la figure suivante).

Historique des modifications d'un objet utilisateur
Historique des modifications d'un objet utilisateur
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Mise en place de l'administration Administrons nos propres modèles

Administrons nos propres modèles

Première prise en main Personnalisons l'administration

Administrons nos propres modèles

Pour le moment, nous avons vu comment manipuler les données des objets de base de Django, ceux concernant les utilisateurs. Il serait pratique désormais de faire de même avec nos propres modèles. Comme dit précédemment, l'administration est auto-générée : vous n'aurez pas à écrire beaucoup de lignes pour obtenir le même résultat que ci-avant. En réalité, quatre lignes suffisent : créez un fichier admin.py dans le répertoire blog/ et insérez ces lignes :

from django.contrib import admin
from blog.models import Categorie, Article

admin.site.register(Categorie)
admin.site.register(Article)

Ici, nous indiquons à Django de prendre en compte les modèles Article et Catégorie dans l'administration. Rafraîchissez la page (relancez le serveur Django si nécessaire) et vous devez voir apparaître une nouvelle section, pour notre blog, semblable à la figure suivante.

La deuxième section nous permet enfin de gérer notre blog !
La deuxième section nous permet enfin de gérer notre blog !

Les fonctionnalités sont les mêmes que celles pour les utilisateurs : nous pouvons éditer des articles, des catégories, les supprimer, consulter l'historique, etc. Vous pouvez désormais créer vos articles depuis cette interface et voir le résultat depuis les vues que nous avons créées précédemment.
Comme vous pouvez le voir, l'administration prend en compte la clé étrangère de la catégorie.

Comment cela fonctionne-t-il ?

Au lancement du serveur, le framework charge le fichier urls.py et tombe sur la ligne admin.autodiscover(). Cette méthode ira chercher dans chaque application installée (celles qui sont listées dans INSTALLED_APPS) un fichier admin.py, et si celui-ci existe exécutera son contenu.
Ainsi, si nous souhaitons activer l'administration pour toutes nos applications, il suffit de créer un fichier admin.py dans chacune, et d'appeler la méthode register() de admin.site sur chacun de nos modèles.
Nous pouvons alors deviner que le module django.contrib.auth contient son propre fichier admin.py, qui génère l'administration des utilisateurs et des groupes.
De même, le module Site, que nous avons ignoré depuis le début, fonctionne de la même façon. Ce module sert à pouvoir faire plusieurs sites, avec le même code. Il est rarement utilisé, et si vous souhaitez le désactiver, il vous suffit de commenter la ligne 5 du code ci-dessous, dans votre settings.py :

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    'blog',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
)
Fatigué(e) de lire sur un écran ? Découvrez ce cours en livre.

Première prise en main Personnalisons l'administration

Personnalisons l'administration

Administrons nos propres modèles Les formulaires

Personnalisons l'administration

Avant tout, créez quelques articles depuis l'administration, si ce n'est déjà fait. Cela vous permettra de tester tout au long de ce chapitre les différents exemples qui seront donnés.

Modifier l'aspect des listes

Dans un premier temps, nous allons voir comment améliorer la liste. En effet, pour le moment, nos listes sont assez vides, comme vous pouvez le constater sur la figure suivante.

Notre liste d'articles, avec uniquement le titre comme colonne
Notre liste d'articles, avec uniquement le titre comme colonne

Le tableau ne contient qu'une colonne contenant le titre de notre article. Cette colonne n'est pas due au hasard : c'est en réalité le résultat de la méthode __unicode__ que nous avons définie dans notre modèle.

class Article(models.Model):
    titre = models.CharField(max_length=100)
    auteur = models.CharField(max_length=42)
    slug = models.SlugField(max_length=100)
    contenu = models.TextField()
    date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de parution")
    categorie = models.ForeignKey(Categorie)

    def __unicode__(self):
        return self.titre

Ce résultat par défaut est assez utile, mais nous aimerions pouvoir gérer plus facilement nos articles : les trier selon certains champs, filtrer par catégorie, etc. Pour ce faire, nous devons créer une nouvelle classe dans notre fichier admin.py, contenant actuellement ceci :

from django.contrib import admin
from blog.models import Categorie, Article

admin.site.register(Categorie)
admin.site.register(Article)

Nous allons donc créer une nouvelle classe pour chaque modèle. Notre classe héritera de admin.ModelAdmin et aura principalement 5 attributs, listés dans le tableau suivant :

Nom de l'attribut

Utilité

list_display

Liste des champs du modèle à afficher dans le tableau

list_filter

Liste des champs à partir desquels nous pourrons filtrer les entrées

date_hierarchy

Permet de filtrer par date de façon intuitive

ordering

Tri par défaut du tableau

search_fields

Configuration du champ de recherche

Nous pouvons dès lors rédiger notre première classe adaptée au modèle Article :

class ArticleAdmin(admin.ModelAdmin):
   list_display   = ('titre', 'auteur', 'date')
   list_filter    = ('auteur','categorie',)
   date_hierarchy = 'date'
   ordering       = ('date', )
   search_fields  = ('titre', 'contenu')

Ces attributs définissent les règles suivantes :

Désormais, il faut spécifier à Django de prendre en compte ces données pour le modèle Article. Pour ce faire, modifions la ligne admin.site.register(Article), en ajoutant un deuxième paramètre :

admin.site.register(Article, ArticleAdmin)

Avec ce deuxième argument, Django prendra en compte les règles qui ont été spécifiées dans la classe ArticleAdmin.

from django.contrib import admin
from blog.models import Categorie, Article

class ArticleAdmin(admin.ModelAdmin):
    list_display   = ('titre', 'auteur', 'date')
    list_filter    = ('auteur','categorie',)
    date_hierarchy = 'date'
    ordering       = ('date', )
    search_fields  = ('titre', 'contenu')

admin.site.register(Categorie)
admin.site.register(Article, ArticleAdmin)

Vous pouvez maintenant observer le résultat sur la figure suivante :

La même liste, bien plus complète, et plus pratique !
La même liste, bien plus complète, et plus pratique !

Les différents changements opérés sont désormais visibles. Vous pouvez bien sûr modifier selon vos besoins : ajouter le champ Catégorie dans le tableau, changer le tri…

Pour terminer, nous allons voir comment créer des colonnes plus complexes. Il peut arriver que vous ayez envie d'afficher une colonne après un certain traitement. Par exemple, afficher les 40 premiers caractères de notre article. Pour ce faire, nous allons devoir créer une méthode dans notre ModelAdmin, qui va se charger de renvoyer ce que nous souhaitons, et la lier à notre list_display.

Créons tout d'abord notre méthode. Celles de notre ModelAdmin auront toujours la même structure :

def apercu_contenu(self, article):
    """ 
    Retourne les 40 premiers caractères du contenu de l'article. S'il
    y a plus de 40 caractères, il faut ajouter des points de suspension.
    """
    text = article.contenu[0:40]
    if len(article.contenu) > 40:
        return '%s...' % text
    else:
        return text

La méthode prend en argument l'instance de l'article, et nous permet d'accéder à tous ses attributs. Ensuite, il suffit d'exécuter quelques opérations, puis de renvoyer une chaîne de caractères. Il faut ensuite intégrer cela dans notre ModelAdmin.

Et comment l'ajoute-t-on à notre list_display ?

Il faut traiter la fonction comme un champ. Il suffit donc d'ajouter 'apercu_contenu' à la liste, Django s'occupe du reste. Pour ce qui est de l'en-tête, il faudra par contre ajouter une ligne supplémentaire pour spécifier le titre de la colonne :

# -*- coding:utf-8 -*-
from django.contrib import admin
from blog.models import Categorie, Article

class ArticleAdmin(admin.ModelAdmin):
    list_display   = ('titre', 'categorie', 'auteur', 'date', 'apercu_contenu')
    list_filter    = ('auteur','categorie',)
    date_hierarchy = 'date'
    ordering       = ('date',)
    search_fields  = ('titre', 'contenu')

    def apercu_contenu(self, article):
        """ 
        Retourne les 40 premiers caractères du contenu de l'article. S'il
        y a plus de 40 caractères, il faut ajouter des points de suspension. 
        """
        text = article.contenu[0:40]
        if len(article.contenu) > 40:
            return '%s...' % text
        else:
            return text

    # En-tête de notre colonne
    apercu_contenu.short_description = u'Aperçu du contenu'

admin.site.register(Categorie)
admin.site.register(Article, ArticleAdmin)

Nous obtenons notre nouvelle colonne avec les premiers mots de chaque article (voir la figure suivante).

La liste des articles est accessible depuis l'administration
La liste des articles est accessible depuis l'administration
Modifier le formulaire d'édition

Nous allons désormais nous occuper du formulaire d'édition. Pour le moment, comme vous pouvez le voir sur la figure suivante, nous avons un formulaire affichant tous les champs, hormis la date de publication (à cause du paramètre auto_now_add=True dans le modèle).

Le formulaire d'édition d'un article par défaut
Le formulaire d'édition d'un article par défaut

L'ordre d'apparition des champs dépend actuellement de l'ordre de déclaration dans notre modèle. Nous allons ici séparer le contenu des autres champs.
Tout d'abord, modifions l'ordre via un nouvel attribut dans notre ModelAdmin : fields. Cet attribut prend une liste de champs, qui seront affichés dans l'ordre souhaité. Cela nous permettra de cacher des champs (inutile dans le cas présent) et, bien évidemment, de changer leur ordre :

fields = ('titre', 'slug', 'auteur', 'categorie', 'contenu')

Nous observons peu de changements, à part le champ Catégorie qui est désormais au-dessus de Contenu (voir la figure suivante).

Notre formulaire, avec une nouvelle organisation des champs
Notre formulaire, avec une nouvelle organisation des champs

Pour le moment, notre formulaire est dans un unique fieldset (ensemble de champs). Conséquence : tous les champs sont les uns à la suite des autres, sans distinction. Nous pouvons hiérarchiser cela en utilisant un attribut plus complexe que fields.
À titre d'exemple, nous allons mettre les champs titre, auteur et categorie dans un fieldset et contenu dans un autre.

fieldsets = (
    # Fieldset 1 : meta-info (titre, auteur…)
   ('Général', {
        'classes': ['collapse',],
        'fields': ('titre', 'slug', 'auteur', 'categorie')
    }),
    # Fieldset 2 : contenu de l'article
    ('Contenu de l\'article', {
       'description':u'Le formulaire accepte les balises HTML. Utilisez-les à bon escient !',
       'fields': ('contenu', )
    }),
)

Voyons pas à pas la construction de ce tuple :

  1. Nos deux éléments dans le tuple fieldset, qui correspondent à nos deux fieldsets distincts.

  2. Chaque élément contient un tuple contenant exactement deux informations : son nom, et les informations sur son contenu, sous forme de dictionnaire.

  3. Ce dictionnaire contient trois types de données :

    1. fields: liste des champs à afficher dans le fieldset ;

    2. description : une description qui sera affichée en haut du fieldset, avant le premier champ ;

    3. classes : des classes CSS supplémentaires à appliquer sur le fieldset (par défaut il en existe trois : wide, extrapretty et collapse).

Ici, nous avons donc séparé les champs en deux fieldsets et affiché quelques informations supplémentaires pour aider à la saisie. Au final, nous avons le fichier admin.py suivant :

# -*- coding:utf-8 -*-
from django.contrib import admin
from blog.models import Categorie, Article

class ArticleAdmin(admin.ModelAdmin):

    # Configuration de la liste d'articles
    list_display   = ('titre', 'categorie', 'auteur', 'date')
    list_filter    = ('auteur','categorie', )
    date_hierarchy = 'date'
    ordering       = ('date', )
    search_fields  = ('titre', 'contenu')

    # Configuration du formulaire d'édition
    fieldsets = (
    	# Fieldset 1 : meta-info (titre, auteur…)
       ('Général', {
            'classes': ['collapse',],
            'fields': ('titre', 'slug', 'auteur', 'categorie')
        }),
        # Fieldset 2 : contenu de l'article
        ('Contenu de l\'article', {
           'description': u'Le formulaire accepte les balises HTML. Utilisez-les à bon escient !',
           'fields': ('contenu', )
        }),
    )

    # Colonnes personnalisées 
    def apercu_contenu(self, article):
        """ 
        Retourne les 40 premiers caractères du contenu de l'article. S'il
        y a plus de 40 caractères, il faut rajouter des points de suspension.
        """
        text = article.contenu[0:40]
        if len(article.contenu) > 40:
            return '%s...' % text
        else:
            return text

    apercu_contenu.short_description = 'Aperçu du contenu'

admin.site.register(Categorie)
admin.site.register(Article, ArticleAdmin)

… qui donne la figure suivante.

Notre formulaire, mieux présenté qu'avant
Notre formulaire, mieux présenté qu'avant
Retour sur notre problème de slug

Souvenez-vous, au chapitre précédent nous avons parlé des slugs, ces chaînes de caractères qui permettent d'identifier un article dans notre URL. Dans notre zone d'administration, ce champ est actuellement ignoré… Nous souhaitons toutefois le remplir, mais en plus que cela se fasse automatiquement !

Nous avons notre champ slug que nous pouvons désormais éditer à la main. Mais nous pouvons aller encore plus loin, en ajoutant une option qui remplit instantanément ce champ grâce à un script JavaScript. Pour ce faire, il existe un attribut aux classes ModelAdmin nommé prepopulated_fields. Ce champ a pour principal usage de remplir les champs de type SlugField en fonction d'un ou plusieurs autres champs :

prepopulated_fields = {'slug': ('titre', ), }

Ici, notre champ slug est rempli automatiquement en fonction du champ titre. Il est possible bien entendu de concaténer plusieurs chaînes, si vous voulez par exemple faire apparaître l'auteur (voir la figure suivante).

Exemple d'utilisation de prepopulated_fields
Exemple d'utilisation de prepopulated_fields
En résumé