Qu'est-ce qu'une interface CRUD ?
Dans la Partie 3, nous avons passé beaucoup de temps à travailler dans le back-end de l'application, en effectuant des opérations CRUD dans le shell et dans le site d'administration.
Dans la Partie 4, nous nous intéresserons à la manière dont nos utilisateurs finaux interagiront avec l'application. Ils voudront faire des choses comme :
consulter la liste des dernières annonces ;
consulter les détails d'une annonce donnée qui les intéresse ;
ajouter leurs propres annonces ;
modifier les détails de leur inscription ;
supprimer leur inscription.
Lorsque nos utilisateurs effectuent ces tâches sur le site, ils ne s'en rendent peut-être pas compte, mais ils effectuent en fait des opérations CRUD : ils créent, lisent, mettent à jour et suppriment des objets.
En fait, la plupart des sites web que vous utilisez sont comme ça. Pensez à la façon dont vous publiez sur les médias sociaux : vous créez des messages, vous les modifiez (ou les mettez à jour) et vous les supprimez occasionnellement. Vous pouvez voir une liste de tous les messages (généralement appelée « flux ») et vous pouvez cliquer sur un message spécifique pour voir plus de détails, comme les réponses et les commentaires.
Ainsi, lorsque nous construisons l'interface utilisateur de notre site, on peut dire que nous construisons une interface CRUD.
Commençons par le « R » de CRUD, Read ou lire, et découvrons les différentes façons d'afficher les données à nos utilisateurs :
une vue en liste, qui affiche une liste de tous les objets d'un modèle avec un minimum de détails, par exemple juste le titre d’une annonce ou juste le nom du groupe ;
une vue détaillée, qui affiche un objet avec tous ses détails et tous les champs affichés.
Comme dans les chapitres précédents, nous allons d'abord examiner les concepts ensemble en utilisant le modèle Band
, puis dans l'exercice vous appliquerez ce que vous aurez appris au modèle Listing
.
Affichez tous les objets dans une vue en liste
Si nous jetons un coup d'œil à notre page hello
, nous pouvons voir qu'elle affiche déjà une liste de groupes.
Il est peut-être temps de mettre de l'ordre dans cette page pour en faire quelque chose que nous utiliserons dans notre application finale.
Tout d'abord, nous allons renommer le fichier modèle. Nous pourrions simplement l'appeler « bands.html », mais à la fin de cette partie, nous aurons beaucoup de modèles liés aux groupes, alors commençons par une convention d'appellation. Le format sera « <nom du modèle type de vue>.html ».
Donc nous renommons hello.html
en band_list.html
.
Maintenant, mettons à jour le contenu du modèle. Ceci va être la vue en liste principale pour nos objets Band
, donc mettons à jour l'en-tête et supprimons le paragraphe :
# listings/templates/listings/band_list.html
{% extends 'listings/base.html' %}
{% block content %}
<h1>Groupes</h1>
<ul>
{% for band in bands %}
<li>{{ band.name }}</li>
{% endfor %}
</ul>
{% endblock %}
Maintenant, mettons à jour la vue. Nous renommerons la fonction en utilisant la même convention que le modèle, et nous la ferons également pointer vers notre modèle nouvellement renommé :
# listings/views.py
def band_list(request): # renommer la fonction de vue
bands = Band.objects.all()
return render(request,
'listings/band_list.html', # pointe vers le nouveau nom de modèle
{'bands': bands})
Enfin, mettons à jour le modèle d'URL. Cette vue est actuellement mappée sur le chemin hello/
, mais utilisons plutôt bands/
:
urlpatterns = [
...
path('bands/', views.band_list), # mise à jour du chemin et de la vue
...
]
Testez la vue dans le navigateur :
![La page web affiche le titre Groupes, suivi d'une liste à puces des 3 noms de groupes.](https://user.oc-static.com/upload/2021/10/27/16353464233655_P4C1a.png)
Nous avons maintenant une vue en liste Band
, affichant tous les objets Band
avec un minimum de détails : elle montre seulement le champ name
.
Affichez un objet dans une vue détaillée
Nous devons choisir un chemin URL approprié pour afficher un objet unique à l'utilisateur. Mais à quoi doit-il correspondre ?
Nous pouvons trouver des idées dans les applications web que nous utilisons déjà. Prenez un moment pour ouvrir vos applications web préférées dans un navigateur et cliquez sur un élément individuel, par exemple, un message spécifique sur les médias sociaux, un article de blog, une photo ou un cours.
Quel genre d'URL voyez-vous dans la barre d'adresse avant et après avoir cliqué ?
Voici quelques exemples :
https://twitter.com/djangoproject/ nous amène au flux Twitter de Django, mais si nous cliquons sur un tweet spécifique, nous allons vers :
https://twitter.com/djangoproject/status/1379372684400861185https://openclassrooms.com/fr/courses nous montre la liste complète des cours sur OpenClassrooms, mais si nous cliquons sur un cours, nous allons sur :
https://openclassrooms.com/fr/courses/7168871-apprenez-les-bases-du-langage-python
Ces exemples varient dans leur approche et je suis sûr que la vôtre le fera aussi, mais une technique souvent utilisée consiste à ajouter une sorte d'identifiant à la fin de l'URL de la vue en liste, afin d'accéder à la vue détaillée.
Si nous suivons le même format et que notre chemin d'accès à la liste est bands/
, alors les détails de nos trois premiers groupes seront situés à l'adresse suivante :
bands/1/
bands/2/
bands/3/
Voyons comment nous pouvons représenter cela dans un nouveau modèle d'URL :
# merchex/urls.py
urlpatterns = [
…
path('bands/', views.band_list, name='band-list'),
path('bands/<int:id>/', views.band_detail), # ajouter ce motif sous notre autre motif de groupes
…
]
Avec ce chemin, nous faisons correspondre « bands/ », suivi de n'importe quel nombre entier, suivi d'un slash final (« / »). Nous « capturons » l'entier du chemin en ajoutant <int:id>
dans le chemin. Le int
est le type de données que nous nous attendons à recevoir : un entier. Le id
devient le nom d'un argument qui est passé à la vue. Créons cette vue maintenant :
# listings/views.py
…
def band_detail(request, id): # notez le paramètre id supplémentaire
return render(request,
'listings/band_detail.html',
{'id': id}) # nous passons l'id au modèle
…
Ensuite, nous devons créer le gabarit.
# listings/templates/listings/band_detail.html
{% extends 'listings/base.html' %}
{% block content %}
<h2>L'identifiant est {{ id }}</h2>
{% endblock %}
Visitons http://127.0.0.1:8000/bands/1/... et ensuite http://127.0.0.1:8000/bands/2/ :
![Une page web avec le chemin /bands/2/ dans le url. La page affiche « L'id est 2 ».](https://user.oc-static.com/upload/2021/10/27/16353465466193_P4C1b.png)
Pouvez-vous voir comment chaque identifiant que vous tapez dans l'URL dans la barre d'adresse est transmis à la vue, puis au modèle ?
Maintenant, nous devons d'une manière ou d'une autre convertir cet id en son objet Band
respectif dans la base de données. Retour à la vue :
# listings/views.py
...
def band_detail(request, id):
band = Band.objects.get(id=id) # nous insérons cette ligne pour obtenir le Band avec cet id
return render(request,
'listings/band_detail.html',
{'band': band}) # nous mettons à jour cette ligne pour passer le groupe au gabarit
...
Nous voyons ici une nouvelle méthode : Band.objects.get()
, dont le but est de retourner un seul objet. Dans notre code, nous écrivons .get(id=id)
, ce qui revient à dire « obtenez-moi l'objet qui a cet id ». Ensuite, nous passons ce band
au modèle. Mettons donc à jour le modèle :
# listings/templates/listings/band_detail.html
{% extends 'listings/base.html' %}
{% block content %}
<h2>{{ band.name }}</h2>
<ul>
<li>Genre : {{ band.genre }}</li>
<li>Année de formation : {{ band.year_formed }}</li>
<li>Actif : {{ band.active }}</li>
<li>{{ band.official_homepage }}</li>
</ul>
<p>{{ band.biography }}</p>
{% endblock %}
Jetons un autre coup d'œil dans le navigateur :
![La page s'intitule De La Soul et affiche dans une liste à puces le genre, l'année, le statut actif et la page d'accueil du groupe.](https://user.oc-static.com/upload/2021/10/27/16353489480397_image44.png)
Et voilà notre page avec les détails sur le groupe !
Nous pourrions avoir des centaines ou des milliers de groupes dans notre base de données et nous pourrions afficher n'importe lequel d'entre eux dans cette vue en plaçant simplement son identifiant dans l'URL.
Hmm, quelque chose ne va pas avec le genre - il affiche « HH » au lieu de « Hip Hop ». On peut réparer ça ?
Bien vu. Heureusement, Django fournit une certaine « magie » que nous pouvons utiliser quand un champ utilise une classe TextChoices
comme c'est le cas de genre
. Mettez à jour cette ligne du modèle :
# listings/templates/listings/band_detail.html
<li>Genre : {{ band.get_genre_display }}</li>
Django a ajouté la méthode get_genre_display
pour convertir les valeurs de la base de données genre
(par exemple HH
) dans leur format d'affichage (par exemple Hip Hop
).
Utilisons également le filtre de gabarit yesno
sur le champ active
, pour convertir « True » en « Yes ». Et nous ferons de la page d'accueil officielle un lien cliquable.
Nous devons aussi nous rappeler que official_homepage
peut être vide (c'est un champ nullable), donc nous devons d'abord vérifier qu'il existe avec une instruction if.
# listings/templates/listings/band_detail.html
<li>Actif : {{ band.active|yesno }}</li>
{% if band.official_homepage %}
<li><a href="{{ band.official_homepage }}">{{ band.official_homepage }}</a></li>
{% endif %}
Liez la vue en liste et la vue détaillée
C'est bien que nous puissions accéder à la page de De La Soul en visitant http://127.0.0.1:8000/bands/1/, mais attendons-nous vraiment de nos utilisateurs qu'ils sachent que « 1 » est l'identifiant de De La Soul et qu'ils le tapent dans la barre d'adresse ? Bien sûr que non.
De nombreux utilisateurs ne regardent même pas la barre d'adresse lorsqu'ils naviguent sur un site. Le fait que certains navigateurs modernes comme Safari cachent désormais le chemin d'accès à l'URL dans la barre d'adresse en est la preuve !
La plupart des utilisateurs s'attendent implicitement à être guidés sur le site par des éléments de navigation intuitifs, comme les hyperliens.
Mettons à jour le modèle de la vue en liste, afin que chaque nom de groupe devienne un lien cliquable qui nous amène à la vue détaillée de ce groupe.
Nous commençons par envelopper le nom du groupe dans un lien :
# listings/templates/listings/band_list.html
...
{% for band in bands %}
<li><a href="">{{ band.name }}</a></li>
{% endfor %}
</ul>
...
Qu'utiliserons-nous pour l'attribut href
?
Eh bien, nous pourrions faire quelque chose comme ça :
# listings/templates/listings/band_list.html
...
<li><a href="/bands/{{ band.id }}/">{{ band.name }}</a></li>
...
Cela va générer un lien fonctionnel : n'hésitez pas à tester ! Mais c'est un anti-pattern. Pourquoi ? Parce que c'est du code répété : nous construisons déjà ce chemin d'une manière similaire dans urls.py :
# merchex/urls.py
path('bands/<int:id>/', ...)
Si nous voulions changer le chemin d'accès à la vue détaillée, par exemple, en « all-bands/1/ », nous devrions le modifier dans urls.py et dans chaque gabarit où nous répétons le lien.
Comment éviter la répétition du code et rendre nos liens plus faciles à maintenir ?
Commençons par donner au modèle d'URL un argument name
:
# merchex/urls.py
path('bands/<int:id>/', views.band_detail, name='band-detail')
J'ai utilisé « band-detail » avec un tiret pour le nom de la vue ici, mais le name
peut être ce que vous voulez.
Le modèle d'URL est maintenant notre « source unique de vérité » pour ce lien. Pour générer ce lien dans notre gabarit, nous allons utiliser une balise de gabarits :
# listings/templates/listings/band_list.html
…
<li><a href="{% url 'band-detail' band.id %}">{{ band.name }}</a></li>
…
Maintenant, si nous rafraîchissons notre liste de groupes, nous pouvons voir que chaque nom de groupe est maintenant un lien, et nous pouvons cliquer sur la vue détaillée du groupe :
![Les trois noms de groupes figurant dans la liste à puces sont accompagnés d'un lien hypertexte.](https://user.oc-static.com/upload/2021/10/27/16353492759885_4c1d.png)
Faisons maintenant la même chose dans l'autre sens : la vue détaillée du groupe doit comporter un lien vers la liste des groupes :
# merchex/urls.py
path('bands/', views.band_list, name='band-list'),
# listings/templates/listings/band_detail.html
<p>{{ band.biography }}</p>
<a href="{% url 'band-list' %}">Retour à tous les groupes</a>
{% endblock %}
![Au bas de la page web, la phrase «Retourner à tous les groupes ».](https://user.oc-static.com/upload/2021/10/27/1635349376093_4c1e.png)
Puisque nous travaillons avec les liens, profitons-en pour ajouter une barre de navigation à notre site, ainsi qu'un lien vers la liste des groupes. Nous l'ajouterons dans le gabarit de base, en tant que premier élément de la balise <body>
, car la barre de navigation doit apparaître en haut de chaque page :
# listings/templates/listings/base.html
...
<body>
<nav>
<a href="{% url 'band-list' %}">Groupes</a>
</nav>
{% block content %}{% endblock %}
</body>
...
![En haut de la page De La Soul se trouve le mot Groupes, avec un lien hypertexte.](https://user.oc-static.com/upload/2021/10/27/16353494807483_4c1f.png)
Si vous souhaitez revoir les étapes précédentes de la création de vues en liste et de vues détaillées, consultez le screencast !
C'est à vous ! Créez des vues en liste et détaillées pour le modèle des annonces
Maintenant, c'est à votre tour de créer des vues en liste et détaillées pour le modèle
Listing
et d'ajouter des liens entre les deux. Ajoutez également un lien vers la vue en liste dans notre barre de navigation.
Une fois que vous aurez fait cela, vous pourrez également ajouter des liens vers toutes les annonces d'un groupe à partir de la page détaillée du groupe. Pour parcourir en boucle les annonces d'un groupe dans le gabarit, vous pouvez utiliser {% for listing in band.listing_set.all %}
.
En résumé
Une vue en liste affiche tous les objets d'un modèle particulier. Nous pouvons obtenir tous les objets avec par exemple
Band.objects.all()
.Une vue détaillée affiche un seul objet, mais avec plus de détails. Nous pouvons obtenir un objet avec par exemple
Band.objects.get(id=un_id)
.Pour créer des liens dans nos gabarits, plutôt que de répéter les chemins URL, nous donnons un
name
à notre modèle d'URL, puis nous utilisons la balise de gabarits{% url %}
pour générer le lien.
Voilà, le « R » pour Read ou Lire de « CRUD » est maintenant vu. Pour toutes les autres opérations, nous avons besoin d'un moyen d'envoyer des données du navigateur au serveur. C'est là que les formulaires entrent en jeu.