gwift-book/source/part-3-django-concepts/views.adoc

6.9 KiB
Raw Blame History

Vues

Une vue correspond à un contrôleur dans le pattern MVC. Tout ce que vous pourrez définir au niveau du fichier views.py fera le lien entre le modèle stocké dans la base de données et ce avec quoi lutilisateur pourra réellement interagir (le template).

De manière très basique, une vue consiste en un objet Python dont le retour sera une instance de type `HttpResponse:

from django.http import HttpResponse

def home(request):
	return HttpResponse("Hello, World!")

Techniquement, une vue pourrait retourner autre chose quune réponse Http, mais cela bloquera au niveau des middlewares, qui sattendent tous à traiter un objet de ce type-là. Les APIs de type REST, SOAP ou GraphQL ne font finalement quune chose: encapsuler leur résultat dans un objet de type HttpResponse.

Le paramètre request est lui de type HttpRequest et embarque énormément dinformations, dont le schéma, les cookies, les verbes http,

Note
détailler les informations de lobjet request :-p

Chaque vue peut etre représentée de deux manières:

  1. Soit par des fonctions,

  2. Soit par des classes.

Le comportement leur est propre, mais le résultat reste identique. Le lien entre lURL à laquelle lutilisateur accède et son exécution est faite au travers du fichier gwift/urls.py, comme on le verra par la suite.

Function Based Views

Les fonctions (ou FBV pour Function Based Views) permettent une implémentation classique des contrôleurs. Au fur et à mesure de votre implémentation, on se rendra compte quil y a beaucoup de répétitions dans ce type dimplémentation: elles ne sont pas obsolètes, mais dans certains cas, il sera préférable de passer par les classes.

Pour définir la liste des WishLists actuellement disponibles, on précédera de la manière suivante:

  1. Définition dune fonction qui va récupérer les objets de type WishList dans notre base de données. La valeur de retour sera la construction dun dictionnaire (le contexte) qui sera passé à un template HTML. On démandera à ce template deffectuer le rendu au travers de la fonction render, qui est importée par défaut dans le fichier views.py.

  2. Construction dune URL qui permettra de lier ladresse à lexécution de la fonction.

  3. Définition du squelette.

# wish/views.py

from django.shortcuts import render
from .models import Wishlist

def wishlists(request):
	wishlists = Wishlist.objects.all()
	return render(
		request,
		'wish/list.html',
		{
			'wishlists': wishlists
		}
	)

Rien quici, on doit déjà tester deux choses:

  1. Quon construit bien le modèle attendu - la liste de tous les souhaits déjà émis.

  2. Que le template wish/list.html existe bien - sans quoi, on va tomber sur une erreur de type TemplateDoesNotExist dans notre environnement de test, et sur une erreur 500 en production.

A ce stade, vérifiez que la variable TEMPLATES est correctement initialisée dans le fichier gwift/settings.py et que le fichier templates/wish/list.html ressemble à ceci:

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<meta http-equiv="x-ua-compatible" content="ie=edge">
		<title></title>
	</head>
	<body>
		<p>Mes listes de souhaits</p>
		<ul>
		{% for wishlist in wishlists %}
			<li>{{ wishlist.name }}: {{ wishlist.description }}</li>
		{% endfor %}
		</ul>
	</body>
</html>

A présent, ajoutez quelques listes de souhaits grâce à un shell, puis lancez le serveur:

$ python manage.py shell
>>> from wish.models import Wishlist
>>> Wishlist.create('Décembre', "Ma liste pour les fêtes de fin d'année")
<Wishlist: Wishlist object>
>>> Wishlist.create('Anniv 30 ans', "Je suis vieux! Faites des dons!")
<Wishlist: Wishlist object>

Lancez le serveur grâce à la commande python manage.py runserver, ouvrez un navigateur quelconque et rendez-vous à ladresse `http://localhost:8000 <http://localhost:8000>`_. Vous devriez obtenir le résultat suivant:

  1. image:: mvc/my-first-wishlists.png :align: center

Rien de très sexy, aucune interaction avec lutilisateur, très peu dutilisation des variables contextuelles, mais cest un bon début! =)

Class Based Views

Les classes, de leur côté, implémente le pattern objet et permettent darriver facilement à un résultat en très peu de temps, parfois même en définissant simplement quelques attributs, et rien dautre. Pour lexemple, on va définir deux classes qui donnent exactement le même résultat que la fonction wishlists ci-dessus. Une première fois en utilisant une classe générique vierge, et ensuite en utilisant une classe de type ListView.

Lidée derrière les classes est de définir des fonctions par convention plutôt que par configuration.

Note
à compléter ici :-)

ListView

Les classes génériques implémentent un aspect bien particulier de la représentation dun modèle, en utilisant très peu dattributs. Les principales classes génériques sont de type ListView, […​]. Limplémentation consiste, exactement comme pour les fonctions, à:

  1. Définir une sous-classe de celle que lon souhaite utiliser

  2. Câbler lURL qui lui sera associée

  3. Définir le squelette.

# wish/views.py

from django.views.generic import ListView

from .models import Wishlist

class WishListList(ListView):
	context_object_name = 'wishlists'
	model = Wishlist
	template_name = 'wish/list.html'

Il est même possible de réduire encore ce morceau de code en définissant juste le snippet suivant :

# wish/views.py

from django.views.generic import ListView

from .models import Wishlist

class WishListList(ListView):
	context_object_name = 'wishlists'

Par inférence, Django construit beaucoup dinformations: si on navait pas spécifié les variables context_object_name et template_name, celles-ci auraient pris les valeurs suivantes:

  • context_object_name: wishlist_list (ou plus précisément, le nom du modèle suivi de _list)

  • template_name: wish/wishlist_list.html (à nouveau, le fichier généré est préfixé du nom du modèle).

En létat, par rapport à notre précédente vue basée sur une fonction, on y gagne sur les conventions utilisées et le nombre de tests à réaliser. A vous de voir la déclaration que vous préférez, en fonction de vos affinités et du résultat que vous souhaitez atteindre.

Note
un petit tableau de différence entre les deux ? :-)
# gwift/urls.py

from django.conf.urls import include, url
from django.contrib import admin

from wish.views import WishListList

urlpatterns = [
	url(r'^admin/', include(admin.site.urls)),
	url(r'^$', WishListList.as_view(), name='wishlists'),
]

Cest tout. Lancez le serveur, le résultat sera identique.