****** Jour 2 ****** 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 attendues d'un répertoire téléphonique. Nous allons donc créer maintenant un mini-moteur de publication de nouvelles qui sera accesible sur le frontal du site. 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ées de publication et de référencement. Le reste des données du model est libre. Créons donc les models, et activons ensuite l'interface d'administration. Une fois que nous aurons une interface d'administration efficace, nous allons nous attaquer au coté front de l'application. Introduction aux vues ===================== Une vue dans Django, est une fonction (ou bien une méthode si on utilise les nouvelles vues génériques), qui recevra en premier argument, un objet représentant la requete HTTP envoyée par le client. Une vue peut recevoir plusieurs paramètres, pour en assurer la réutilisation. Une vue doit toujours retourner une réponse HTTP. Les vues répresentent par exemple une vue en liste d'un model, ou sa vue détaillée. Elles peuvent aussi très bien réprésenter le résultat d'une recherche, ou des formulaires d'éditions, des fichiers à télécharger, tout ce qui est permis par le protocole HTTP. Exemple de vue: :: from django.http import HttpResponse from npublisher.models import Entry def last_entries(request): latest_entry_list = Entry.objects.order_by('-creation_date')[:5] output = ', '.join([entry.title for title in latest_entry_list]) return HttpResponse(output) Configuration des urls ---------------------- Il faut maintenant mapper cette vu a une url. Dans le répertoire de notre application *npublisher* nous allons donc créer un fichier nommé ``urls.py`` contenant ce bout de code : :: """Urls of npublisher""" from django.conf.urls.defaults import * urlpatterns = patterns('npublisher.views', url(r'^last_entries/$', 'last_entries', name='npublisher_last_entries'), ) Notez que ce fichier d'URLs peut être inclus dans le fichier d'URLs de votre projet, grâce à la fonction ``include``, exemple : :: url(r'^informations/', include('npublisher.urls')), Vue avec parametres ------------------- On veut modifier la vue pour qu'elle puisse prendre en options une date et afficher les derniers Entry d'une date précisé dans l'url. Puis une vue qui affiche une Entry spécifique a partir de son slug. Utiliser les vue génériques =========================== Ecrire des vues avec l'habitude devient une tâche longue et répétive. Par exemple, il est souvent nécessaire d'écrire la vue en liste puis, la vue détaillée d'un model précis. Heureusement Django peut palier à ce problème grâce à une collection de vues génériques incorporée dans sa distribution. Ces vues permettent dans la majorité des cas d'écrire peu de code Python, ce qui a un avantage certain. :: from django.views.generic.list import ListView from django.views.generic.detail import DetailView class EntryListView(ListView): model = Entry class EntryDetailView(DetailView): model = Entry Et dans les urls: :: url(r'^entries/$', EntryListView.as_view(), name='npublisher_entries_list'), url(r'^entry/(?P[-\w]+)/$', EntryDetailView.as_view(), name='npublisher_entry_detail'), Il peut arriver des cas, où les vues génériques ne répondent pas tout à fait à notre besoin. Par exemple si nous voulons faire une vue filtrant les entrées par catégories, nous retournons toujours des entrées, mais la *queryset* ne sera pas bonne car la notion de filtre de ne pourra pas être appliquée. Nous allons donc creer une autre ListView :: class EntryListByCategoryView(ListView): model = Entry def get_queryset(self): category = get_object_or_404(Category, slug=self.args[0]) return Entry.objects.filter(categories=category) Et dans les urls: :: url(r'^entries/(?P[-\w]+)/$', EntryListByCategoryView.as_view(), name='npublisher_entries_by_category_list'), Cette vue va retrouver la catégorie dont le slug est passé en paramètre, ce qui va nous permettre de générer une queryset filtrant les entrées de cette catégorie. Les templates ============= Actuellement nous avons le code, c'est à dire la logique de l'application, mais pas de 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 manquant est défini par le comportement des vues génériques, qui vont prendre le nom de l'application suivis d'un '/' puis du nom du model utilisé avec pour finir *_detail.html* ou *_list.html* en fonction de la vue appelée. Mais il est possible de spécifier un autre nom de template au besoin. Nous allons donc créer les templates qui manquent. Au sein de notre application, nous allons créer un répertoire nommé ``templates``, puis créer un sous répertoire nommé lui ``npublisher``. Une fois dans le répertoire ``npublisher`` du dossier ``templates``, nous allons 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'apparaît plus, mais en fait rien n'apparait. Ceci est normal car pour l'instant nos templates sont vides. Remarques --------- * Django possède plusieurs systèmes d'acquisition des templates. Par défaut, 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 mécanisme va vérifier dans chaques applications installées si un dossier nommé ``templates`` existe pour l'inclure dans la recherche de templates. C'est ici la technique utiliser. * 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'acquistion prioritaire. * Il est donc aisé de fournir un jeu de templates par défaut à une application réutilisable, tout en permettant un haut degré de personalisation. Philosophie des templates ------------------------- 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 en encore du PDF si on le veut. Les templates ne doivent pas incorporer trop de logiques, sauf celles de la présentation, le reste doit être délégué aux vues ou aux models en eux meme. Maintenant réalisons nos templates. Centralisation et héritage dans les templates --------------------------------------------- Le système de templating dans Django est assez puissant pour permettre l'héritage entre templates. Cette fonctionnalitée évite la redondance de certains éléments présent entre les différents templates. Dans le cas d'un site internet, c'est éléments, seront par exemple les métas de publication, la navigation, le footer, etc. Inutile de copier coller, ou d'inclure, il suffit d'hériter. Technique à 3 niveaux --------------------- Il est possible de faire à peu prêt ce que l'on veut avec le système de template, mais il existe une technique d'organisation qui allie personalisation et robustesse, nommée technique à 3 niveaux. Dans la section ``TEMPLATE_DIRS`` du fichier ``settings.py`` de votre projet, nous allons spécifier un répertoire d'acquisition. Il est d'usage de créer un répertoire nommé ``templates`` au sein même de notre projet, et de spécifier celui ci en priorité. Dans ce dossier nous allons ensuite créer un fichier nommé ``base.html``, celui-ci contiendra le squelette HTML de notre site web. Autours des zones dont le contenu changera, nous allons poser des tags **block**. Ces tag block sont souvent les mêmes, exemple : * la zone de CSS * la zone de Javascript * la balise title * la zone de contenu * la navigation Exemple : :: Mon site {% block title%}{% endblock %} 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èdement. 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`` : :: {% extends "npublisher/base.html" %} {% block title %}{{ block.super }} Entrées{% endblock %} Il suffit ensuite de définir la zone de contenu à afficher dans le *block* représentant le contenu dans le template principal. Si une modification doit etre apportée sur tout le module au niveau du design, il suffit juste de modifier le fichier ``base.html`` de l'application. Création de managers ==================== Maintenant que nous avons nos vues, nous remarquons un problème, même les entrées marquées du champs **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 nous fait trainer du code qui devrait faire partie de la logique de l'objet. Une autre solution serait de modifier les **managers** de notre model *Entry*. Chaque model possède un manager par défaut, nommé **objects** par défaut, 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 entrées publiées et seulement celles-là. Dans notre fichier ``models.py``, nous allons donc 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 comment un model retrouve ses instances en base de données. Dans notre classe *Entry* il faut désormais rajouter ces lignes : :: objects = models.Manager() published = EntryPublishedManager() Et maintenant dans le fichier ``view.py`` de notre application, il faut changer la queryset par celle ci : :: class EntryListView(ListView): model = Entry queryset = Entry.published.all() Désormais seules les entrées publiées seront retournées par la vue. Remarque -------- Nous voyons que nous avons rajouté à la première ligne de notre model, l'attribut ``objects``. Ce n'est pas obligatoire, mais il est nécessaire de l'utiliser pour assurer une compatibilité avec les autres applications de notre site. Pourquoi le déclarer alors ? Car dans le fonctionnement des models, la première classe hérité de ``models.Manager``, va devenir le *manager* par défaut de notre model. C'est un comportement à prendre en compte lors de l'ajout d'un nouveau manager. Travaux pratiques ================= Vous allez maintenant créer une nouvelle application, réutilisable. Le but de cette application sera de pouvoir créer et publier des galleries de photos sur notre projet web.