• Aucun résultat trouvé

Une liste d'objets en quelques lignes avec ListView

Commençons par une simple liste de nos articles, sans pagination. À l'instar de TemplateView, nous pouvons utiliser ListView directement en lui passant en paramètre le modèle à traiter :

Code : Python - blog/urls.py

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

from django.views.generic import ListView

from blog.models import Article urlpatterns = patterns('',

# Nous allons réécrire l'URL de l'accueil url(r'^$', ListView.as_view(model=Article,)), # Et nous avons toujours nos autres pages…

url(r'^article/(?P<id>\d+)$', 'blog.views.lire'), url(r'^(?P<page>\d+)$', 'blog.views.archives'),

url(r'^categorie/(?P<slug>.+)$', 'blog.views.voir_categorie'), )

Avec cette méthode, Django impose quelques conventions :

Le template devra s'appeler <app>/<model>_list.html. Dans notre cas, le template serait nommé blog/article_list.html.

L'unique variable retournée par la vue générique et utilisable dans le template est appelée object_list, et contiendra ici tous nos articles.

Il est possible de redéfinir ces valeurs en passant des arguments supplémentaires à notre ListView :

Code : Python - blog/urls.py

urlpatterns = patterns('',

url(r'^$', ListView.as_view(model=Article,

context_object_name="derniers_articles", template_name="blog/accueil.html")), ...

)

Par souci d'économie, nous souhaitons réutiliser le template blog/accueil.html qui utilisait comme nom de variable derniers_articles à la place d'object_list, celui par défaut de Django.

Vous pouvez dès lors supprimer la fonction accueil dans views.py, et vous obtiendrez le même résultat qu'avant (ou presque, si vous avez plus de 5 articles) ! L'ordre d'affichage des articles est celui défini dans le modèle, via l'attribut ordering de la sous-classe Meta, qui se base par défaut sur la clé primaire de chaque entrée.

Il est possible d'aller plus loin : nous ne souhaitons généralement pas tout afficher sur une même page, mais par exemple filtrer les articles affichés. Il existe donc plusieurs attributs et méthodes de ListView qui étendent les possibilités de la vue. Par souci de lisibilité, nous vous conseillons plutôt de renseigner les classes dans views.py, comme vu précédemment. Tout d'abord, changeons notre urls.py, pour appeler notre nouvelle classe :

Code : Python - urls.py

from blog.views import ListeArticles urlpatterns = patterns('',

url(r'^$', ListeArticles.as_view(), name="blog_categorie"), # Via la fonction as_view, comme vu tout à l'heure

...

Pourquoi avoir nommé l'URL avec l'argument name ?

Pour profiter au maximum des possibilités de Django et donc écrire les URL via la fonction reverse, et son tag associé dans les templates. L'utilisation du tag url se fera dès lors ainsi : {% url "blog_categorie" categorie.id %}. Cette fonctionnalité ne dépend pas des vues génériques, mais est inhérente au fonctionnement des URL en général. Vous pouvez donc également associer le paramètre name à une vue normale.

Ensuite, créons notre classe qui reprendra les mêmes attributs que notre ListView de tout à l'heure :

Code : Python - blog/views.py

class ListeArticles(ListView): model = Article

context_object_name = "derniers_articles"

template_name = "blog/accueil.html"

Désormais, nous souhaitons paginer nos résultats, afin de n'afficher que 5 articles par page, par exemple. Il existe un attribut adapté :

Code : Python - blog/views.py

class ListeArticles(ListView): model = Article

context_object_name = "derniers_articles"

template_name = "blog/accueil.html" paginate_by = 5

De cette façon, la page actuelle est définie via l'argument page, passé dans l'URL (/?page=2 par exemple). Il suffit dès lors d'adapter le template pour faire apparaître la pagination. Sachez que vous pouvez définir le style de votre pagination dans un template séparé, et l'inclure à tous les endroits nécessaires, via {% include pagination.html %} par exemple.

Le fonctionnement par défaut de la pagination est celui de la classe Paginator, présent dans django.core.paginator. Référez-vous au chapitre sur ce sujet.

Code : Jinja - blog/accueil.html

<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>

{% endfor %}

{# Mise en forme de la pagination ici #} {% if is_paginated %}

<div class="pagination">

{% if page_obj.has_previous %}

<a href="?page={{ page_obj.previous_page_number }}">Précédente</a> —

{% endif %}

Page {{ page_obj.number }} sur {{ page_obj.paginator.num_pages }} {% if page_obj.has_next %}

— <a href="?page={{ page_obj.next_page_number }}">Suivante</a>

{% endif %}

</div>

Allons plus loin ! Nous pouvons également surcharger la sélection des objets à récupérer, et ainsi soumettre nos propres filtres :

Code : Python - views.py

class ListeArticles(ListView): model = Article

context_object_name = "derniers_articles"

template_name = "blog/accueil.html"

paginate_by = 5

queryset = Article.objects.filter(categorie__id=1)

Ici, seuls les articles de la première catégorie créée seront affichés. Vous pouvez bien entendu effectuer des requêtes identiques à celle des vues, avec du tri, plusieurs conditions, etc. Il est également possible de passer des arguments pour rendre la sélection

un peu plus dynamique en ajoutant l'ID souhaité dans l'URL. Code : Python - blog/urls.py

from django.conf.urls import patterns, url

from blog.views import ListeArticles urlpatterns = patterns('blog.views',

url(r'^categorie/(\w+)$', ListeArticles.as_view()), url(r'^article/(?P<id>\d+)$', 'lire'),

url(r'^(?P<page>\d+)$', 'archives') )

Dans la vue, nous sommes obligés de surcharger get_queryset, qui renvoie la liste d'objets à afficher. En effet, il est impossible d'accéder aux paramètres lors de l'assignation d'attributs comme nous le faisons depuis le début.

Code : Python - views.py

class ListeArticles(ListView): model = Article

context_object_name = "derniers_articles"

template_name = "blog/accueil.html"

paginate_by = 10 def get_queryset(self):

return Article.objects.filter(categorie__id=self.args[0])

Tâchez tout de même de vous poser des limites, le désavantage ici est le suivant : la lecture de votre urls.py devient plus difficile, tout comme votre vue (imaginez si vous avez quatre arguments, à quoi correspond le deuxième ? le quatrième ?). Enfin, il est possible d'ajouter des éléments au contexte, c'est-à-dire les variables qui sont renvoyées au template. Par exemple, renvoyer l'ensemble des catégories, afin de faire une liste de liens vers celles-ci. Pour ce faire, nous allons ajouter au tableau

context une clé categories qui contiendra notre liste : Code : Python - blog/views.py

def get_context_data(self, **kwargs):

# Nous récupérons le contexte depuis la super-classe context = super(ListeArticles,

self).get_context_data(**kwargs)

# Nous ajoutons la liste des catégories, sans filtre particulier

context['categories'] = Categories.objects.all() return context

Il est facile d'afficher la liste des catégories dans notre template :

Code : Jinja - Extrait de blog/accueil.html

<h3>Catégories disponibles</h3> <ul>

{% for categorie in categories %}

<li><a href="{% url "blog_categorie" categorie.id %}">{{ categorie.nom }}</a></li>

{% endfor %}

</ul>