Django est un framework, il était au départ développé pour un
environnement de revue de presse électronique appartenant à World Online.
Puis en Juillet 2005 il a été abstrait et publié comme framework web libre.
Cet héritage est apparent dans l'ensemble du paquetage : dans la
documentation, dans l'interface d'admin, et dans la bibliothèque
standard, par exemple les types de champs gérés.
Django inclut tout les composants nécessaires au développement de
n'importe quelle application web de base :
Correspondance objet-relationnel pour la base de données
Un système de squelette séparant le code Python du code HTML
Django n'utilise pas de serveur d'application tierce partie comme
CherryPy. Il fournit un script gestionnaire qui démarre un de
développement simple. Il s'appuie cependant sur un serveur web avec
mod_python ou FastCGI pour le déploiement en production.
Les différentes couches du framework ne devraient pas
se connaître les unes les autres à moins que cela soit
absolument nécessaire.
Moins de code
Les applications Django devraient utiliser aussi peu de code que
possible; elles devraient rester agiles. Django devrait profiter
pleinement des possibilités dynamiques de Python, tel que l'introspection.
Développement rapide
L'intérêt d'un framework du 21ème siècle est de rendre rapide les
tâches fastidieuses du développement Web. Django devrait permettre
un développement Web incroyablement rapide.
Ne vous répétez pas (Don't Repeat Yourself)
Chaque concept/portion de données distincts devrait résider en un,
et un seul, endroit. La redondance est mal. La normalisation est
bien.
Explicite est meilleur qu'implicite
Ceci, un principe au cur de Python, veut dire que Django ne
devrait pas faire de choses trop magiques. La magie ne devrait
pas apparaître tant qu'il n'y a pas de raison valable pour
cela. La magie vaut la peine d'être employée seulement si elle
amène un grand confort, inaccessible d'une autre manière, et si
elle n'est pas implémentée de manière à rendre confus les
développeurs qui essayent d'apprendre à utiliser la fonctionnalité.
Cohérence
Le framework devrait être cohérent à tout niveau. La cohérence
s'applique à tout élément depuis le bas niveau jusqu'au haut niveau.
Les modèles devraient encapsuler chaque aspect d'un objet, selon
le patron de conception Active Record de Martin Fowler.
C'est pour cette raison que les options d'administration spécifiques
au modèle sont incluses dans le modèle lui même; les données
relatives au modèle devraient être stockées dans le modèle.
Elle devrait exécuter des requêtes SQL le moins souvent possible, et
elle devrait optimiser les requêtes en interne.
Syntaxe succincte mais puissante
L'API de base de données devrait autoriser de riches et consistantes
requêtes dans un minimum de syntaxe possible. Elle ne devrait pas
s'appuyer sur l'import d'autres modules ou d'objets d'aide à la mise
en forme.
Les jointures devraient être réalisées automatiquement, en coulisse,
lorsque nécessaire.
Une option pour exécuter une requête SQL facilement
L'API de base de données devrait réaliser qu'elle est un raccourci
mais pas nécessairement un tout-en-un. Le framework devrait rendre
facile l'écriture de SQL sur mesure -- des requêtes complètes, ou
juste des clauses WHERE personnalisées en tant que paramètres pour
les appels à l'API.
Le système d'URL Django devrait permettre aux URLs provenant de la
même application d'être différentes dans des contextes
différents. Par exemple, un site peut mettre les articles à
/articles/, alors qu'un autre peut utiliser /nouvelles/.
Flexibilité infinie
Les URLs devraient aussi flexibles que possible. Toute conception
d'URL imaginable devrait être permise.
Encourager les bonnes pratiques
Le framework devrait faire en sorte qu'il soit facile (ou plus
facile) pour un développeur de concevoir de jolies URLs que des
mauvaises.
Les extensions de fichier dans les URLs des pages Web devraient être
évitées.
URLs permanentes
Techniquement, foo.com/bar et foo.com/bar/ sont deux URLs
différentes, et les robots des moteurs de recherche (et quelques
outils d'analyse du traffic) pourraient les traiter de manière
séparée. Django devrait faire un effort pour "normaliser" les URLs
afin ne pas permettre la confusion aux robots des moteurs de
recherche.
Nous voyons un système de template comme un outil contrôlant la
présentation et la logique relative à la présentation -- et c'est
tout. Le système de template ne devrait pas supporter de
fonctionnalités qui vont au delà de ces principes de base.
Décourager la redondance
L'héritage au sein des templates, permet de se passer de la
duplication de certains éléments du contenu, tel le footer ou le header.
Etre dissocié du HTML
Le système de template ne devraient pas être conçu pour seulement
produire du HTML. Il devrait être également bon à générer d'autres
formats basés sur du texte, ou juste du texte.
Sûreté et sécurité
Le système de template, nativement, devrait interdire l'inclusion de
code malicieux -- tel que des commandes supprimant les données en
base.
Extensibilité
Le système de template devrait prendre en compte le fait que les
auteurs de templates de niveau avancé peuvent vouloir étendre cette
technologie.
C'est le concept derrière les marqueurs et les filtres de template
personnalisés.
L'écriture d'une vue devrait être aussi simple que d'écrire une
fonction Python. Les développeurs ne devraient pas avoir besoin
d'instancier une classe quand une fonction suffit.
Couplage faible
Une vue ne devrait pas se soucier du système de template que le
développeur utilise -- ou même si un système de template est utilisé
ou non.
Différencier entre GET et POST
GET et POST sont distincts; les développeurs devraient explicitement
utiliser l'un ou l'autre. Le framework devrait facilement distinguer
les données GET et POST.
Maintenant il est nécessaire de créer un répertoire qui stockera vos
projets, comme cela les développements seront centralisés. Une fois
dans ce répertoire, exécutez la commande suivante
Si nous regardons le contenu du répertoire de notre projet, nous y
retrouvons plusieurs choses :
__init__.py
Ce fichier permet de dire à l'interpréteur python que ce répertoire
est un module python. C'est la norme.
manage.py
Script python reprenant les fonctionnalités de django-admin.py et
qui servira aux tâches d'administration de notre projet
settings.py
Ce fichier va contenir toute la configuration de notre projet.
Il sert de point d'entrée à notre projet. Ecrit en python, ce
fichier de configuration est très souple à manipuler.
urls.py
Comme son nom l'indique, ce fichier contiendra les urls associées à
notre projet. Il est le squelette du projet, faisant la liaison
entre le code et les urls.
Au final nous pouvons donc dire qu'un projet Django est un module
python permettant l'administration et le contrôle des différents
composants intervenant dans le projet. Composants appelés Applications
Un projet étant constitué d'applications, Django fournit un utilitaire
pour créer rapidement la structure d'une application au sein du
projet, grâce à cette commande
$> python manage.py startapp monapplication
On remarque que nous utilisons désormais le script manage.py, mais
cette action est aussi valable grâce au script django-admin.py.
Une application est un simple module python. La meilleure preuve est
de regarder le contenu du répertoire monapplication. Les fichiers
suivants sont présents :
__init__.py
Ceci est le fichier qui prouve que le répertoire est un module
python, car sa présence l'indique à l'interpréteur.
models.py
Module python contenant les classes python relatives aux models
de l'application. Utiliser ce fichier pour écrire ses models est la
norme.
views.py
Idem que pour models sauf qu'ici seront écrites les vues de
l'application.
Une application Django est donc un simple module python définissant un
ensemble de fonctions et de classes, qui seront utilisées au sein de
notre projet.
Il est important de savoir qu'il existe 2 manières de développer une
application :
Coupled
Une application dite couplée est typiquement une application créée par la
commande startapp au sein de notre projet. Elle va lier
l'application avec notre projet, ce qui aura l'inconvénient de
devoir spécifier précisément les chemins d'importation de
l'application et d'en limiter grandement la réutilisabilité.
Dans le cas d'un développement spécifique, ceci ne pose pas de
problème, mais il n'est recommandé de procéder ainsi.
Standalone
Une application Standalone est une application indépendante du
projet, qui pourra résider n'importe où dans $PYTHON_PATH.
Dans le cas d'une application réutilisable et/ou distribué, il est
nécessaire de procéder ainsi.
Nous utiliserons cette méthode tout au long des travaux pratiques.
Un model est un objet dans Django servant à interagir avec une base de
données. Il doit contenir toutes les propriétés et méthodes de notre
objet représenté. Un model pouvant avoir des relations avec d'autres
models, ces relations devront êtres aussi décrites.
Les models serviront de structure à notre application, et en
définiront une bonne partie de la logique.
C'est pour cette raison qu'il est nécessaire de commencer nos
applications par une conception solide de nos models.
Nous devons maintenant construire nos models qui vont donc définir la
logique de notre application. Notre application sera constituée de 2
models, l'un représentant un contact et un autre représentant ses
numéros de téléphones.
Editons le fichier
repertoire_telephonique/models.py pour qu'il ressemble à cela :
Maintenant que nous possédons, nos propres models, il est nécessaire
de les installer.
Installer les models d'une application, revient à installer
l'application.
Pour cela, il faut éditer les ficher settings.py de votre projet,
et y rajouter 'repertoire_telephonique', dans la section INSTALLED_APPS
Maintenant nous allons établir une synchronisation entre les models et
notre base de données. Cette synchronisation servira à créer les tables
SQL décrites par vos models.
Les models écrits vont servir d'interface avec la base de données. Les
attributs de classe définis au sein du model serviront de colonnes et
définiront le type. Ce sont les Fields
Il existe des Fields pour à peut prêt chaque utilisation, il est même
possible de créer ses propres Fields.
Le système d'url au sein de Django est une des possibilités les plus
intéressantes de Django pour les raisons suivantes :
Motif
Les urls sont constituées de motif permettant la variation et
l'écriture de n'importe quelles urls et le passage de paramètres de
manière élégante.
Rapidité
Les urls sont compilées au démarrage du serveur pour un gain de temps.
Loose Coupling
Les urls sont décentralisées, et permettent l'association entre le
motif de l'url et une vue.
Il est bon de savoir que les urls peuvent être nommées, pour un souci
de clarté et aussi pour éviter d'avoir un couplage fort au sein de
vos templates et vues. Nous verrons cela en détail plus tard.
Une des fonctionnalités les plus utiles au sein de Django, et qui a
permis au framework de se diffuser rapidement est sans conteste
l'application d'administration, qui est rapide et flexible pour une
mise en place.
L'application d'administration est un module python fourni de base
dans la distrbution de Django, se situant dans le répertoire
contrib de notre installation de Django. Nous pouvons voir dans ce
dossier d'autres applications fournies avec les sources de Django.
Une des premières choses à effectuer est d'activer le module
d'administration. Pour cela dans votre fichier settings.py de votre
projet ajouter 'django.contrib.admin' à l'intérieur de la section
INSTALLED_APPS.
Pour continuer son installation il est nécessaire d'associer une url
aux vues du module d'administration. Le fichier urls.py situé dans
votre projet contient déjà un exemple d'urls pour accéder à
l'administration, mais il est commenté. Décommentez les lignes
suivantes, pour avoir ceci :
>>> from django.contrib import admin
>>> admin.autodiscover()
>>> (r'^admin/(.*)', admin.site.root),
Ensuite exécutez la commande suivante, pour synchroniser les models
contenus dans l'application admin afin d'en assurer son
fonctionnement.
$> python manage.py syncdb
Désormais, si on lance le serveur de développement contenu dans
Django, par la présente commande,
$> python manage.py runserver
on peut accéder à l'interface d'administration grâce à cette url :
http://localhost:8000/admin
Une fois identifié vous pouvez voir une liste des différentes
applications installées sur le serveur et qui sont administrables.
Par contre, nous ne voyons pas notre application au sein de
l'administration, ce qui est plutôt dommage, donc nous allons remédier
à cela.
Pour activer nos applications aux seins de l'administration c'est très
simple, il suffit d'enregistrer nos models dans le module
d'administration.
Pour cela nous allons créer un module admin.py au sein de notre
application de répertoire téléphonique.
$> cd repertoire_telephonique/
$> touch admin.py
Editons le pour qu'il ressemble à cela :
>>> from django.contrib import admin
>>>
>>> from repertoire_telephonique.models import Contact
>>> from repertoire_telephonique.models import Phone
>>>
>>> admin.site.register(Contact)
>>> admin.site.register(Phone)
Désormais si on relance le serveur de développement, et que l'on
recharge la page d'administration, nous y voyons apparaitre notre
application avec ses tables prêtes à être administrées.
Le résultat est plus basique, mais il est possible d'aller plus loin
dans la configuration de l'administration de nos models.
Pour atteindre un résultat où les vues d'administration soient efficaces
et faciles à administrer, il est souvent nécessaire de les configurer.
Il nous faut créer différentes classes définissant le comportement de
chacun de nos models dans l'administration. Ajoutons ce code dans
notre fichier admin.py.
Pour illustrer la suite de mes propos, nous allons avoir besoin de
créer une application plus conséquente, car le répertoire
téléphonique est déjà complet grâce au module d'administration qui
remplit toutes les fonctionnalités souhaitées d'un répertoire
téléphonique.
Nous allons donc créer un mini moteur de publication de nouvelles sur
le site en frontal. Cette application portera le nom de npublisher.
Nous nous imposerons cette liste de fonctionnalités :
gestion des catégories
gestion des auteurs
gestion de la publication, visible ou non
facilité de publication et de référencement
Le reste des données du model est libre.
Créons donc les models, et activons l'interface d'administration.
Une fois que nous avons une interface d'admin efficace, nous allons
nous attaquer à la vue en frontal.
Une vue dans django, est une fonction, ou bien une méthode si on aime
se compliquer la vie, qui recevra en premier argument, un objet
représentant la requête HTTP envoyée par le client. Une vue peut
recevoir plusieurs paramètres, pour en assurer la réutilisabilité.
Une vue doit toujours retourner une réponse de type HTTP.
Les vues représentent par exemple une vue en liste d'un model, ou sa
vue détaillée, elle peut aussi très bien représenter le résultat d'une
recherche, ou des formulaires d'éditions, des fichiers à télécharger,
tout ce qui permet par le protocole HTTP.
Ecrire des vues avec l'habitude devient une tâche longue et répétitive,
par exemple, il est souvent nécessaire de représenter des vues en
liste puis une vue détaillée.
Heureusement Django peut palier à ce problème grâce à un système de vues
génériques incorporé dans sa distribution.
Ses vues permettent dans la majorité des cas de ne pas taper de code
python, avantage certain.
Pour utiliser les vues génériques, tout va se passer aux niveaux des
urls. Dans notre répertoire de notre application npublisher nous
allons créer un fichier nommé urls.py contenant ce code :
Désormais grâce au nommage des urls, il est possible d'avoir un
couplage faible au niveau du models, tout en lui définissant une url
absolue, utile elle aussi pour la notion de couplage faible. Seul le
fichier urls.py de votre projet se maitre de chaque url utilisée
dans votre projet, grâce à un mécanisme d'inversement.
Utilisons cela sur notre model Entry et ajoutons cette méthode :
Actuellement nous avons le code, c'est à dire la logique de
l'application, mais pas le rendu. Si nous testons les urls que nous
avons créées, elles retournent une erreur spécifiant que le template
npublisher/entry_list.html n'existe pas.
Le nom du template recherché est défini par le comportement des vues
génériques, qui vont prendre le nom de l'application suivi d'un '/'
puis du nom du model utilisé pour finir _detail.html ou _list.html en
fonction de la vue appelée.
Nous allons donc créer les templates qui manquent.
Au sein de notre application, nous allons créer un dossier nommé
templates, puis créer un autre dossier nommé lui npublisher.
Une fois dans le répertoire npublisher du dossier templates, on va
créer les templates manquant.
$> cd npublisher
$> mkdir -p templates/npublisher
$> cd templates/npublisher
$> touch entry_list.html
$> touch entry_detail.html
Si nous actualisons notre navigateur, l'erreur n'apparait plus, mais
rien n'apparait, car il ny a rien dans les fichiers templates.
Django possède plusieurs systèmes d'acquisition de templates, le
premier étant celui qui va regarder dans la liste de dossiers
définis dans la section TEMPLATE_DIRS de votre fichier settings.py.
Un autre moyen est de vérifier dans le dossier de vos
applications installées si un dossier templates existe pour
l'inclure dans les recherches. C'est ici la technique utilisée.
Grâce à ces différentes techniques d'acquisition, il est possible
de modifier le rendu d'une application par défaut, en dupliquant le
template visé pour le modifier et le mettre dans un chemin
d'acquisition prioritaire.
Il est donc aisé de fournir un jeu de templates à une application
réutilisable, pour ensuite les remplacer en fonction des besoins de
personnalisation.
Un template, sert juste à mettre en forme des données. C'est pourquoi
le système de template est tout aussi bien capable de produire du
HTML, du CSV ou bien encore du PDF.
Les templates ne doivent pas incorporer trop de logique, sauf celle
de la présentation, le reste doit être délégué aux vues ou aux models
en eux mêmes.
Le système de template dans Django est assez puissant pour permettre
l'héritage entre templates. Cette fonctionnalité évite la redondance
de certains éléments communs à différents templates.
Dans le cas d'un site internet, ces éléments, seront les métas de
publication, la navigation, le footer. Inutile de copier coller, ou
d'inclure, il suffit d'hériter.
Il est possible de faire à peut prêt ce que l'on veut, avec le système
de template, mais une technique robuste et souple au niveau de la
personnalisation est retenue. Celle de la technique à 3 niveaux.
Dans la section TEMPLATE_DIRS du fichier settings.py de votre projet,
il faut spécifier au moins un dossier. Il est d'usage de faire un dossier
templates au sein de votre projet, et de spécifier ce dernier.
Dans ce dossier nous allons créer un fichier nommé base.html, celui
ci contiendra le design html de notre charte graphique.
Autours des zones qui seront dynamiques, nous allons établir des
blocks. Ces blocks sont souvent les mêmes :
la zone de css
la zone de javascript
la balise title
la zone de contenu
la navigation
Exemple
<title>Mon site {% block title%}{% endblock %}</title>
Ensuite nous allons créer dans le dossier templates/npublisher de notre
application un fichier nommé aussi base.html ce fichier héritera de
notre fichier base.html créer précédemment. Ensuite il pourra
surcharger les blocks hérités de son parent.
{% extends "base.html" %}
{% block title %}NPublisher{% endblock %}
Désormais dans chacun des templates de notre application, nous
hériterons de application/base.html qui nous fournira une
structure de template déjà configurée. Par exemple dans
entry_list.html :
Il peut arriver des cas, où les vues génériques ne répondent pas toute
à fait à notre utilisation. Par exemple si je veux faire une vue
filtrant les entrées par catégorie, je vais toujours retourner des
entrées, mais la queryset ne sera pas bonne car la notion de filtre
ne sera pas appliquée.
Dans ce cas il est possible d'écrire une vue générique wrappée.
Nous allons donc créer un fichier views.py dans le répertoire de
notre application. Il devrait ressembler à cela :
>>> """Views of npublisher"""
>>> from django.shortcuts import get_object_or_404
>>> from django.shortcuts import render_to_response
>>> from django.views.generic.list_detail import object_list
>>>
>>> from npublisher.models import Category
>>>
>>> def category_view_entry(request, slug):
>>> """Return the view for entry of a category"""
>>> category = get_object_or_404(Category, slug=slug)
>>> return object_list(request, queryset=category.entry_set.all(),
>>> extra_context={'slug': slug})
Cette vue sélectionne la catégorie dont le nom est passé dans l'url,
et ensuite nous retournons une vue générique correctement formatée avec
la queryset correspondante, c'est à dire les entrées associées à la
catégorie.
Utiliser le système de vue générique permet de s'abstenir de beaucoup de
codes redondant, et permet de rester constant au sein de notre
application.
Cette vue doit maintenant être associée à une url, donc dans notre
fichier urls.py de l'application npublisher nous rajoutons ceci :
Maintenant que nous avons nos vues, nous remarquons un problème, même
les vues marquées du tag visible à False sont affichées dans la
vue en liste.
Une solution serait de modifier la queryset passée en paramètre dans
les vues génériques, mais cela ne serait pas très pratique dans
l'utilisation, car cela fait trainer du code qui devrait faire parti
de la logique de l'objet.
L'autre solution serait de modifier le manager de notre models
Entry. Chaque models possède un manager par défaut, il se nomme
objects, mais il possible d'en ajouter d'autres pour répondre à
différents besoins.
Nous allons donc créer un manager pour notre model Entry, qui
permettra de retrouver rapidement les publiés et seulement celle la.
Dans notre fichier models, il faut rajouter cette classe, pour que
cela fonctionne.
>>> class EntryPublishedManager(models.Manager):
>>> """Manager to retrieve published entries"""
>>>
>>> def get_query_set(self):
>>> return super(EntryPublishedManager, self).get_query_set().filter(visible=True)
Cette classe hérite de models.Manager, et surcharge le comportement de
la méthode get_query_set qui définit le moyen dont un model retrouve
ses données.
Dans notre classe Entry il faut désormais rajouter ces lignes :
>>> objects = models.Manager()
>>> published = EntryPublishedManager()
Et désormais dans le fichier urls.py de notre application, il faut
changer le dictionnaire entry_conf par ceci :
Nous voyons que nous avons rajouté à la première ligne, l'attribut
objects. Ce n'est pas obligatoire, mais il est nécessaire de
l'utiliser pour assurer une compatibilité avec les autres modules.
Pourquoi le déclarer alors ?
Car dans le fonctionnement des models, la première classe héritée de
models.Manager, va devenir le manager par défaut de notre
models. C'est une feature à prendre en compte lors de la conception
de manager.
Il est parfois nécessaire de créer ses propres tags pour une
utilisation au sein des templates. Par exemple, il serait utile sur la
page d'accueil du site d'afficher les 5 dernières informations
postées. Puis sur une partie de la page, les catégories disponibles.
Cette logique dépend dans ce cas du template, et non de la vue, qui
elle dans notre cas, ne devrait que retourner un template.
Comment procéder à cela ? Il suffit de créer un tag utilisable au sein
de nos templates.
Dans notre application nous allons créer un module python appelé
templatetags et qui va contenir nos tags. C'est Django qui s'occupe de
charger en mémoire nos tags lors du démarrage du serveur en regardant
le répertoire templatetags de chaque application installée.
$> cd npublisher/
$> mkdir templatetags
$> cd templatetags/
$> touch __init__.py
$> touch getter.py
getter.py contiendra le tag qui nous permettra de récupérer n'importe
quel objet au sein de nos templates.
>>> from django.template import Library, Node
>>> from django.db.models import get_model
>>>
>>> register = Library()
>>>
>>> class LatestContentNode(Node):
>>> def __init__(self, model, num, varname):
>>> self.num, self.varname = int(num), varname
>>> self.model = get_model(*model.split('.'))
>>>
>>> def render(self, context):
>>> if self.num == -1:
>>> context[self.varname] = self.model._default_manager.all()
>>> else:
>>> context[self.varname] = self.model._default_manager.all()[:self.num]
>>> return ''
>>>
>>> def get_latest(parser, token):
>>> bits = token.contents.split()
>>> if len(bits) != 5:
>>> raise TemplateSyntaxError, "get_latest tag takes exactly four arguments"
>>> if bits[3] != 'as':
>>> raise TemplateSyntaxError, "third argument to get_latest tag must be 'as'"
>>> return LatestContentNode(bits[1], bits[2], bits[4])
>>>
>>> get_latest = register.tag(get_latest)
Ce tag va charger dans le contexte du template, les derniers objets de
n'importe quelle application. Voici un exemple de code pour un
fichier template.
{% load getter %}
{% get_latest npublisher.entry 5 as entry_list %}
{% for entry in entry_list %}
<a href="{{ entry.get_absolute_url }}">{{ entry }}</a>
{% endfor %}
Parfois, il est nécessaire de créer ses propres contextes, dans le cas
où des informations sont redondantes à travers le site. Par exemple,
un fil d'ariane.
Nous allons donc voir comment utiliser nos propres contextes, appelés
context template processor.
Nous allons donc dans notre application créer un fichier
context_processors.py contenant le code suivant :
Maintenant dans le fichier settings.py de notre application, il est
nécessaire d'ajouter cette section, car elle surcharge la
configuration par défaut et doit donc la rappeler :
Les templates context processors sont utiles dès que des
informations ont besoin d'êtres affichées de manière redondante et en
fonction de la requête HTTP.
Internationaliser son application est une problèmatique que l'on
retrouve souvent lors de la conception d'un projet. Il est nécessaire
de savoir quelles parties d'une application devront être
internationalisées, et dans quelles langues.
Ceci peut rapidement être fastidieux, mais heureusement Django fournit
tous les outils pour rendre cette tâche plus aisée.
Un des autres avantages principaux de l'internationalisation, est
d'éviter de coder des fichiers python avec des caractères accentués,
par exemple au niveau des models, car cela peut devenir vite un
problème lors de la publication des données.
Internationnaliser son code est souvent la première étape lors de
l'internationalisation de votre application.
Il vous suffit de marquer chaque chaine qui devra être traduite,
lors de l'écriture de votre code. En fonction du contexte la méthode a
employé peut varier. Dans les models nous utiliserons des traductions
à la volée, alors que dans les vues, les traductions peuvent être faites
au lancement du serveur.
Prenons un exemple d'un model avant internationalisation :
Internationaliser le code n'est souvent que la première étape, car
vos templates devront être aussi internationalisées.
Pour cela, il va falloir charger la machine de traduction à
l'intérieur des templates, et comme pour l'étape précèdente, marquer
les strings qui seront traduites.
{% load i18n %}
<h1>{% trans "news" %}</h1>
<p>{% trans "reference" %} : {{ object.reference }}</p>
Parfois, il arrive que le contenu de la base de données ait besoin
d'être lui même internationalisé. Prenons par exemple le cas de
catégories insérées en base de données. Les catégories elles aussi ont
besoin d'être traduites, mais Django ne fournit pas en standard un
moyen de modéliser ce besoin en base de données.
Mais la communauté veille, et plusieurs applications existent pour
répondre à ce besoin. Par exemple Multilingual
Django est bien pensé, le système d'acquisition de la langue de
l'utilisateur est plutôt efficace. Par défaut il va vérifier s'il
existe une valeur en session définissant la langue de l'utilisateur,
le cas échéant, il va vérifier la présence d'un cookie qui définit les
préférences de langue de l'utilisateur.
Si ces étapes ne retournent rien, il va regarder s'il existe une
traduction pour la valeur Accept-Language de votre navigateur.
Arrivé jusque là, si il n'y a toujours pas de traduction, Django va
afficher la traduction définie par la valeur LANGUAGE_CODE de votre
fichier settings.py.
Ce système permet de toujours trouver une traduction, laissant le choix
aux préférences de l'utilisateur.
<form action="/i18n/setlang/" method="post">
<input name="next" type="hidden" value="/next/page/" />
<select name="language">
{% for lang in LANGUAGES %}
<option value="{{ la
ng.0 }}">{{ lang.1 }}</option>
{% endfor %}
</select>
<input type="submit" value="Change" />
</form>
L'appel de cette vue va créer un cookie spécifiant la préférence de
l'utilisateur et le rediriger vers la vue précèdente si la valeur
next n'est pas présente.
Désormais nous avons tout le système mis en place pour avoir un site
internationnalisé, mais il manque le plus important, les traductions.
Pour cela, nous allons créer un dossier locale soit au niveau
de notre projet, soit de notre application, en fonction du besoin.
$> mkdir locale
Ensuite nous allons exécuter une commande qui va collecter tous
les élements à traduire pour en faire un fichier PO, qui est la norme
pour tout ce qui touche a l'internationalisation.
$> django-admin makemessages -l fr
Cette commande va créer les répertoires et le fichier PO qui
contiendra la traduction française de notre projet ou application.
Si nous allons dans le dossier locale/fr/LC_MESSAGES, là se trouve
notre fichier de traduction.
Nous devons l'éditer pour fournir les traductions, un simple éditeur
de texte, peut faire l'affaire. Mais je conseille par exemple
GTranslator qui fournit une interface efficace pour rapidement
construire son fichier de traduction.
Maintenant que nous avons notre fichier PO préparé, il va être
nécessaire de le compiler pour pouvoir l'utiliser dans notre
application.
$> django-admin compilemessages
Il est aussi envisageable de le faire à la main avec la commande :
La génération de formulaires est une tâche fastidieuse, tant au niveau
de l'écriture, qu'au niveau des contrôles.
Heureusement Django contient un module très utile pour la génération et
l'utilisation de formulaires au sein de pages webs.
La première étape est de modéliser un formulaire de façon pythonique
pour pouvoir ensuite s'en servir comme objet de manipulation.
Créons un formulaire qui va servir d'enregistrement sur le
site.
>>> from django import forms
>>>
>>> class RegisterForm(forms.Form):
>>> login = forms.CharField(max_length=50)
>>> password_1 = forms.CharField(max_length=50, widget=forms.PasswordInput)
>>> password_2 = forms.CharField(max_length=50, widget=forms.PasswordInput)
Désormais nous pouvons utiliser ce formulaire dans nos vues.
Mais il est aussi possible d'écrire soit même son formulaire en
HTML, pour pouvoir en modifier le rendu visuel. Libre à vous, mais il
faudra penser à inclure les cas où certains champs seront erronés.
Nous avons donc un rendu de notre formulaire, mais il n'y a aucun
contrôle effectué sur le contenu du formulaire. Il serait sain
d'établir des contrôles tels que le login soit disponible et non
enregistré, et que les 2 mots de passe, soient identiques.
Pour cela rien de plus simple, il suffit juste d'écrire certaines
méthodes dans notre classe de formulaire.
>>> class RegisterForm(forms.Form):
>>> ...
>>> def clean_login(self):
>>> try:
>>> #mon code de test
>>> pass
>>> except:
>>> forms.ValidationError(_("Your login is already in use !))
>>> return self.cleaned_data['login']
>>>
>>> def clean(self):
>>> if self.cleaned_data.get('password_1') != self.cleaned_data.get('password_2'):
>>> raise forms.ValidationError(_("Your passwords didn't match !"))
>>> return self.cleaned_data
Lors de la validation du formulaire, Django va effectuer une
vérification préliminaire sur les types de champs utilisés, puis si on
écrit une méthode clean_FIELD, la méthode sera appliquée sur le
champ pour faire un contrôle.
Si tout se déroule bien la méthode clean est appelée, et effectue
une validation sur tous les champs.
Comme nous le voyons, la structure des formulaires est assez souple
pour permettre beaucoup de personnalisation.
La dernière étape consiste à écrire une vue qui pourra traiter le
formulaire. Plusieurs méthodes sont envisageables, mais il y a la
bonne méthode et la mauvaise. Voici un exemple de bonne méthode :
>>> def subscribe(request):
>>> if request.POST:
>>> form = RegisterForm(request.POST, label_suffix=' :')
>>> if form.is_valid():
>>> form.save()
>>> return HttpResponseRedirect('/roadshow/thankyou/')
>>> else:
>>> form = RegisterForm(label_suffix=' :')
>>> return render_to_response('subscribe.html', {'form': form})
La bonne méthode comporte les point suivants :
Vérifier que le contenu fournis est bien en POST
Créer et utiliser la méthode save au sein des formulaires, par
convention.
Toujours rediriger avec une page une fois les données traitées pour
éviter les doubles injections et autres problèmes de sécurité.
Le serveur de développement n'est pas une bonne initiative pour
déployer son application. Comme son nom l'indique il sert au
développement. En effet il n'est pas conçu pour servir à grande
échelle les fichiers, sans compter en terme sécurité les
lacunes existantes. Apache ou Lighttp remplissent très bien ce rôle.
Nous allons voir le cas avec Apache et ModPython. Dans votre répertoire
sites-available nous allons écrire une configuration Apache.
Il reste cependant une chose à régler, comment servir les fichiers
statiques de votre projet. Django peut très bien faire cela, mais une
fois de plus, cette solution n'est ni adaptée ni optimisée, car Django
sert à exécuter du code python et non à servir des fichiers.
Donc pour régler ce problème nous allons modifier la configuration
Apache pour que ce soit Apache qui serve les fichiers statiques.
Dans la section VirtualHost de votre configuration, il suffit de
rajouter ceci pour chaque répertoire à servir.
Alias /css /repertoire/de/fichiers/static/css
<Location "/css">
SetHandler None
</Location>
Documenter son application est vital. Cette méthode permet aux
utilisateurs du code ou aux contributeurs de pouvoir rentrer plus
facilement dans le projet. Le mieux est de documenter en même temps
que l'on code, car faire une documentation une fois le projet fini
est souvent fastidieux voire oublié.
Les docstrings en Python sont entourées de triple quote, ce qui permet
un formatage libre au niveau des retours à la ligne et des doubles ou
simple quotes.
Tout ce qui se trouve entre les triples quotes fait parti de la
docstring de la fonction, qui décrit ce que fait la fonction. Une
docstring, si elle existe, doit être la première chose déclarée dans une
fonction (la première chose après les deux points). Techniquement
parlant, vous n'êtes pas obligés de donner une docstring à votre
fonction, mais vous devriez toujours le faire.
Beaucoup d'IDE Python utilisent les docstrings pour fournir une
documentation contextuelle, ainsi lorsque vous tapez le nom d'une
fonction, sa docstring apparaît dans une bulle d'aide. Cela peut être
incroyablement utile, mais cette utilité est liée à la qualité de
votre docstring.
Un exemple :
>>> def mafonction(a, b):
>>> """Docstring de mafonction
>>> Cette fonction ne fait rien"""
>>> pass
Django intègre dans sa version 1.0 un module lié au module
d'administration permettant une review des models, des tags, des vues
et des filtres. Ce module s'appelle django.contrib.admindocs, il est
nécessaire de le rajouter dans la section INSTALLED_APPS, et de lui
lier une url.
Documenter son application, c'est l'ouvrir aux autres. Il
sera plus facile d'acquérir d'autres contributeurs si votre
application est bien documentée.
Bien entendu les docstrings ne font pas tout, rien ne vous empêche par
exemple de rédiger un fichier texte décrivant comment installer votre
application, comment l'utiliser, ce qu'il reste à faire, ou à
debugger. Libre à vous.
Tester une application est vital. Une application non testée, est une
application mort-née. Ici nous ne parlons pas des tests effectués par
l'utilisateur final, mais des tests sur le comportement du code.
Plus une application va se développer et s'étendre en terme de code,
plus les sources de bugs seront importantes. Pourquoi ne pas limiter
les risques en encadrant le tout par des tests.
Les tests permettent de cibler les sources de bug plus facilement,
donnent un regard neuf sur la qualité du code, et surtout offrent un filet
de sécurité indéniable, permettant d'éviter les régressions.
Il existe 2 types de tests : les tests unitaires et les tests fonctionnels.
Les fonctionnels sont souvent effectués par l'utilisateur final, il
existe des frameworks pour établir ces tests, mais étant donné les
divers types d'applications disponibles, nous ne nous attarderons pas sur
ce type.
Les tests unitaires, permettent de tester une ou plusieurs parties de
l'application, ils assurent la cohérence de chaque fonction ou
méthode écrite dans l'application.
Prenons un exemple simple : si je code une fonction effectuant une
addition, il est utile d'écrire un test, vérifiant que la fonction
retourne le bon résulat.
>>> def add(a, b):
>>> return a + b
>>> add(1, 41)
42
Bien évidemment rédiger tous ces tests de manière cohérente, est très
long, heureusement, il existe des frameworks pour nous simplifier la
tâche. Le plus populaire pour les tests unitaires en python est
unittest, il est dérivé du framework JUnit écrit en Java.
Les doctests sont une variante de l'écriture des tests, elles
combinent les avantages de la documentation et des tests.
Rien de mieux que d'avoir sa documentation contenant des tests.
La rédaction de ces tests se fait à l'intérieur des docstrings.
Utiliser un gestionnaire de versions, pour le développement d'une
application d'envergure avec plusieurs collaborateurs est nécessaire.
Cela permet à chaque développeur d'avoir une copie du code, de
pouvoir la modifier et une fois les modifications effectuées de les
publier pour que les autres développeurs puissent en bénéficier.
Dans notre cas, SVN, possède la notion de tags et de branches, qui
permettent de figer un développement, ou bien de forker un
développement lourd pour ensuite le merger avec le code principal,
une fois le code stabilisé.