• 20 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 12/12/19

Trouvez des données

Log in or subscribe for free to enjoy all this course has to offer!

Dans le chapitre précédent vous avez appris comment effectuer des requêtes sur la base de données en utilisant l'ORM de Django. Dans ce chapitre vous les utiliserez dans les vues !

Nous allons mettre à jour toutes nos vues pour qu'elles affichent des informations issues de la base de données.

Importez de nouveau les modèles nécessaires :

views.py

from .models import Album, Artist, Contact, Booking

La page d'accueil

La page d'accueil doit afficher tous les albums marqués comme étant disponibles. Nous avons vu dans le chapitre précédent comment récupérer tous les items en fonction de la valeur d'une des colonnes. Vous en souvenez-vous ? Il s'agit de la méthode filter() qui prend en paramètre le nom de la colonne et la valeur. Comme ceci :

Album.objects.filter(available=True)

Vous pouvez aller plus loin et limiter le nombre d'albums renvoyés. L'instruction suivante renverra les douze premiers résultats :

Album.objects.filter(available=True)[:12]

Enfin, utilisez la méthode order_by() pour trier les albums par date de création. Afin d'inverser l'ordre, et de renvoyer en premier celui qui a été créé en dernier, ajoutez le symbole - au début :

Album.objects.filter(available=True).order_by('-created_at')[:12]

Mettez à jour la vue :

views.py

def index(request):
# request albums
albums = Album.objects.filter(available=True).order_by('-created_at')[:12]
# then format the request.
# note that we don't use album['name'] anymore but album.name
# because it's now an attribute.
formatted_albums = ["<li>{}</li>".format(album.title) for album in albums]
message = """<ul>{}</ul>""".format("\n".join(formatted_albums))
return HttpResponse(message)

La page qui affiche tous les albums

Utilisez une requête similaire à celle de la page d'accueil mais ne limitez pas les résultats.

views.py

def listing(request):
albums = Album.objects.filter(available=True)
formatted_albums = ["<li>{}</li>".format(album.title) for album in albums]
message = """<ul>{}</ul>""".format("\n".join(formatted_albums))
return HttpResponse(message)

La page de détail d'un album

Vous avez accès à l'identifiant d'un album grâce aux paramètres de l'URL. Utilisez la méthode get() qui prend en paramètre la clé primaire d'un objet (qui est, par défaut, l'identifiant). Si l'objet demandé est trouvé, il est renvoyé.

album = Album.objects.get(pk=album_id)

Puis retrouvez les artistes ! Vous pouvez utiliser l'attribut artists qui renvoie la liste des artistes. Sauf que l'objet renvoyé n'est pas une liste ! Il s'agit d'une instance de la classe ManyRelatedManager et il est impossible d'itérer avec.

Comment faire ? Ajoutez la méthode all() !

artists = album.artists.all()

Mettez à jour la vue detail :

views.py

def detail(request, album_id):
album = Album.objects.get(pk=album_id)
artists = " ".join([artist.name for artist in album.artists.all()])
message = "Le nom de l'album est {}. Il a été écrit par {}".format(album.title, artists)
return HttpResponse(message)

La page de recherche

Changeons légèrement le comportement de la page de recherche. Si aucun paramètre n'est passé dans l'URL, affichez plutôt tous les albums :

views.py

def search(request):
query = request.GET.get('query')
if not query:
albums = Album.objects.all()

Puis trouvez dans la documentation de Django comment remplacer les lignes suivantes :

albums = [
album for album in ALBUMS
if query in " ".join(artist['name'] for artist in album['artists'])
]

Réponse : vous pouvez utiliser un filtre sur le titre ! Comme ceci :

albums = Album.objects.filter(title=query)

Le problème est que Django cherchera une correspondance exacte entre la requête et le titre de l'album. Que se passera-t-il si l'utilisateur entre une partie du titre ? L'album ne sera pas trouvé.

Pour éviter ce souci, ajoutez contains à la suite du nom de l'attribut. Comme ceci :

albums = Album.objects.filter(title__contains=query)

A savoir que contains est sensible à la casse. Autrement dit, "My Name Is Stain" est différent de "my Name Is Stain". Utilisez plutôt icontains dans ce cas :

albums = Album.objects.filter(title__icontains=query)

Intéressons-nous maintenant au reste de la méthode :

def search(request):
...
else:
# title contains the query and query is not sensitive to case.
albums = Album.objects.filter(title__icontains=query)
if len(albums) == 0:
message = "Misère de misère, nous n'avons trouvé aucun résultat !"
else:
albums = ["<li>{}</li>".format(album['name']) for album in albums]

albums étant un objet QuerySet, vous vous dites certainement "gardons la méthode len" pour vérifier qu'il n'y a pas de résultat. Holà, manant ! Cela générerait une seconde requête SQL tout à fait inutile puisque vous avez déjà les résultats. Utilisez plutôt la méthode exists() :

if not albums.exists():
message = "Misère de misère, nous n'avons trouvé aucun résultat !"

Vous avez presque terminé mais vous pouvez aller encore plus loin ! Si la recherche au niveau des albums n'a donné aucun résultat, cherchez de nouveau dans la table Artist. Cela permettra d'afficher un formulaire de recherche qui trouve les albums à la fois par leur nom et par celui de leur auteur.

albums = Artist.objects.filter(name__icontains=query)

Néanmoins, une erreur apparaîtra ! Avez-vous deviné pourquoi ? Plus tard, vous utilisez la variable albums ainsi :

albums = ["<li>{}</li>".format(album.title) for album in albums]

Autrement dit, vous avez besoin d'un objet Album et non d'un Artist ! Il faut donc chercher à travers les relations d'un album.

albums = Album.objects.filter(artists__name__icontains=query)

Mettez à jour la vue :

views.py

def search(request):
query = request.GET.get('query')
if not query:
albums = Album.objects.all()
else:
# title contains the query and query is not sensitive to case.
albums = Album.objects.filter(title__icontains=query)
if not albums.exists():
albums = Album.objects.filter(artists__name__icontains=query)
if not albums.exists():
message = "Misère de misère, nous n'avons trouvé aucun résultat !"
else:
albums = ["<li>{}</li>".format(album.title) for album in albums]
message = """
Nous avons trouvé les albums correspondant à votre requête ! Les voici :
<ul>{}</ul>
""".format("</li><li>".join(albums))
return HttpResponse(message)

C'est le moment de tester que tout fonctionne !

Les requêtes, ces planquées

Vous verrez parfois apparaître l'expression "queries are lazy" ou "les requêtes sont paresseuses". En effet, les requêtes ne sont effectuées que lorsque vous utilisez un objet et non lorsque vous demandez à effectuer la recherche. Elles sont différées ! Cela permet à l'ORM de Django d'optimiser les requêtes et d'effectuer le moins d'actions possibles sur la base de données.

Prenons l'exemple suivant :

qs = Author.objects.all()
qs = qs.filter(username="regis")
for author in qs:
print(author)

A votre avis, à quel moment la requête dans la base de données est-elle réalisée ? L'ORM va interroger la base au dernier moment, à la ligne 4, car nous souhaitons afficher des informations sur l'objet cherché.

Ceci a permis à l'ORM d'optimiser la requête !

qs = Author.objects.all() # requête non faite
qs = qs.filter(username="regis") # requête toujours non faite
for author in qs: # requête réalisée
print(author)

Cette partie est terminée ! La base de données fonctionne et vous êtes en mesure d'y effectuer des recherches. Il est temps d'enjoliver l'application ! Nous voyons cela dans la prochaine partie.

Code de ce chapitre

Retrouvez l'intégralité du code de ce chapitre sur le dépôt GitHub.

Example of certificate of achievement
Example of certificate of achievement