Il y a un truc que je trouve bien dans Django, ce sont les vues génériques. Ce sont des fonctions qui permettent d'afficher une liste ou le détail d'objets de façon très simple, sans avoir à coder quoi que ce soit.
Cependant, elles ont un petit inconvénient : par défaut, elles ne permettent pas de filtrer la liste de résultats selon un argument. C'est donc génial pour afficher tous les billets d'un blog ou un billet en détail mais pas pour les billets de la catégorie XYZ. J'en vois déjà certains raler (non pas de nom, c'est mal) . Et bien ils auraient tord, car on peut vite retrouver nos petits avec les vues génériques et un chouilla de dev autour.
Il existe un très bon billet à ce sujet : Django tips: get the most out of generic views
Dans le cas présent, ce qui nous intéresse, c'est d'afficher tous les billets d'un blog portant le tag "XYZ" :
Soit une appli de blog possédant le modèle suivant :
class Tag(models.Model):
name = models.CharField(maxlength=50)
url = models.SlugField(prepopulate_from=("name",))
class Post(models.Model):
author = models.ForeignKey(User)
title = models.CharField(maxlength=50)
summary = models.TextField(blank=True)
message = models.TextField()
tag = models.ManyToManyField(Tag, blank=True)
url = models.SlugField(prepopulate_from=("title",))
et la configuration d'url suivante :
urlpatterns = patterns('',
(r'^$', 'django.views.generic.list_detail.object_list', dict(queryset= Post.objects.all(),)),
(r'^post/(?P<slug>[-\w]+)/$', 'django.views.generic.list_detail.object_detail', dict(queryset= Post.objects.all() , slug_field= 'url',)),
(r'^tag/$', 'django.views.generic.list_detail.object_list', dict(queryset= Tag.objects.all(), )),
(r'^tag/(?P<mytag>[-\w]+)/$', 'atomecms.blog.views.tag_detail'),
)
ce qui nous intéresse surtout, c'est la règle suivant qui dit que toute url commençant par /tag/quelquechose doit être traiter par la vue "tag_detail" de l'application blog de mon projet atomecms :
(r'^tag/(?P<mytag>[-\w]+)/$', 'atomecms.blog.views.tag_detail'),
dans le fichier views.py de mon application, il me suffit de définir la vue "tag_detail" dont le but est d'afficher la liste des billets portant le tag passé dans l'url :
from atomecms.blog.models import Post, Tag
from django.views.generic.list_detail import object_list
# Display all post for a given tag
def tag_detail(request, mytag):
whoistag = Tag.objects.get(url=mytag)
postlist = whoistag.post_set.all()
return object_list(request, queryset=postlist, template_name='blog/tag_detail.html', allow_empty = 'True',)
Explications :
- J'ai besoin de manipuler les objets Post et Tag définis plus haut, je les importe donc,
- Comme je m'appuie sur les vues génériques, j'ai aussi besoin d'importer la fonction me retournant une liste d'objets
- whoistag me permet de récupérer les information du tag passé dans l'url en le matchant avec l'attribut "url" de mon modèle. Il me fait une sorte de lookup complet. J'obtiens ainsi l'id et le nom du tag.
- postlist me donne la liste des billets portant le tag passé dans l'url en me basant sur l'id du tag obtenu précédemment. J'avais écrit initialement la ligne suivante qui donne le même résultat mais est peut être plus compréhensible à savoir que postlist me donnait tous les billets ayant l'id du tag retenu.:
postlist = Post.objects.filter(tag=whoistag.id)
mais david me dit que c'est mieux ainsi et plus "django compliant". En effet, après avoir regardé la doc sur l'api de la DB, on comprend mieux la raison d'être du post_set et le raccourci que me fait faire david.
et dans mon template, dans la zone dédié à cet effet, il me suffit d'ajouter :
{% for object in object_list %}
<dl>
<dt>{{object.title}}</dt>
<dd>{{object.summary}}</dd>
</dl>
{% endfor %}
Explications :
- ma vue me retourne un
object_listavec tout les billets répondant à la requête et fournit pour chaque billet, l'ensemble des champs de mon modèle post. - je crée donc une boucle, qui parcourt mon objet
object_listet pour chaque item j'affiche le titre et le résumé de mon billet.
Et voila, il me suffit de me rendre sur http://localhost:8000/blog/tag/xyz et ça marche ! J'ai l'affichage qui m'intéressait et le projet Atome vient de faire un grand bond en avant ! 
edit : Niko, c'est plus clair maintenant ?