• 40 hours
  • Medium

Free online content available in this course.

Paperback available in this course

course.header.alt.is_certifying

You can get support and mentoring from a private teacher via videoconference on this course.

Got it!

Last updated on 10/11/19

Votre première page grâce aux vues

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

Dans ce chapitre, nous allons créer notre première page web avec Django. Pour ce faire, nous verrons comment créer une vue dans une application et la rendre accessible depuis une URL. Une fois cela fait, nous verrons comment organiser proprement nos URL afin de rendre le code plus propre et structuré. Nous aborderons ensuite deux cas spécifiques des URL, à savoir la gestion de paramètres et de variables dans celles-ci, et les redirections, messages d'erreur, etc.

Cette partie est fondamentale pour aborder la suite et comprendre le fonctionnement du framework en général. Autrement dit, nous ne pouvons que vous conseiller de bien vous accrocher tout du long !

Hello World !

Commençons enfin notre blog sur les bonnes crêpes bretonnes. Au chapitre précédent, nous avons créé une application « blog » dans notre projet, il est désormais temps de se mettre au travail !

Pour rappel, comme vu dans la théorie, chaque vue se doit d'être associée au minimum à une URL. Avec Django, une vue est représentée par une fonction définie dans le fichier views.py, prenant en paramètre une requête HTTP et renvoyant une réponse HTTP. Cette fonction va généralement récupérer des données dans les modèles (ce que nous verrons plus tard) et appeler le bon template pour générer le rendu HTML adéquat. Par exemple, nous pourrions donner la liste des 10 derniers articles de notre blog au moteur de templates, qui se chargera de les insérer dans une page HTML finale, qui sera renvoyée à l'utilisateur.

Pour débuter, nous allons réaliser quelque chose de relativement simple : une page qui affichera « Bienvenue sur mon blog ! ».

La gestion des vues

Chaque application possède son propre fichierviews.py, regroupant l'ensemble des fonctions que nous avons introduites précédemment. Comme tout bon blog, le nôtre possèdera plusieurs vues qui rempliront diverses tâches, comme l'affichage d'un article par exemple.

Commençons à travailler dansblog/views.py. Par défaut, Django a généré gentiment ce fichier :

from django.shortcuts import render

# Create your views here.

Nous pouvons créer une fonction dans ce fichier qui remplira le rôle de la vue. Bien que nous n'ayons vu pour le moment ni les modèles, ni les templates, il est tout de même possible d'écrire une vue complète, mais celle-ci restera basique. Nous allons écrire du code HTML directement dans la vue et le renvoyer au client. Nous ignorons temporairement la méthoderender déjà importée, et utilisons HttpResponse  qui permet de construire une réponse HTTP :

from django.http import HttpResponse
from django.shortcuts import render

def home(request):
    """ Exemple de page non valide au niveau HTML pour que l'exemple soit concis """
    return HttpResponse("""
        <h1>Bienvenue sur mon blog !</h1>
        <p>Les crêpes bretonnes ça tue des mouettes en plein vol !</p>
    """)

Ce code se divise en trois parties :

  • Nous importons la classe HttpResponsedu module django.http. Cette classe permet de retourner une réponse (texte brut ou HTML comme ici) depuis une chaîne de caractères.HttpResponse  est spécifique à Django et permet d'encapsuler votre réponse dans un objet plus générique, que le framework peut traiter plus aisément.

  • Une fonction home, avec comme argument une instance de HttpRequest. Nous avons nommé ici (et c'est partout le cas par convention) sobrement cet argument request. Celui-ci contient des informations sur la méthode de la requête (GET, POST), les données des formulaires, la session du client, etc. Nous y reviendrons plus tard.

  • Finalement, la fonction retourne une instance de HttpResponse  à partir d'une chaîne de caractères contenant du HTML, que la fonction renvoie ensuite au framework.

Toutes les fonctions prendront comme premier argument un objet du type HttpRequest. Toutes les vues doivent forcément retourner une instance de HttpResponse, sans quoi Django générera une erreur.

Par la suite, ne renvoyez jamais du code HTML directement depuis la vue comme nous le faisons ici. Passez toujours par des templates, ce que nous introduirons au chapitre suivant. Il s'agit de respecter l'architecture du framework dont nous avons parlé dans la partie précédente afin de bénéficier de ses avantages (la structuration du code notamment). Nous n'avons utilisé cette méthode que dans un but pédagogique afin d'avancer progressivement.

Routage d'URL : comment j'accède à ma vue ?

Nous avons désormais une vue opérationnelle, il n'y a plus qu'à l'appeler depuis une URL. Mais comment ? En effet, nous n'avons pas encore défini vers quelle URL pointait cette fonction. Pour ce faire, il faut modifier le fichier urls.py  de votre projet (ici crepes_bretonnes/urls.py). Par défaut, uniquement l'administration est configurée, et le fichier contient des exemples basiques :

"""crepes_bretonnes URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/dev/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
[...]
"""
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

Quand un utilisateur appelle une page de votre site, la requête est directement prise en charge par le contrôleur de Django qui va chercher à quelle vue correspond cette URL. En fonction de l'ordre de définition dans le fichier précédent, la première vue qui correspond à l'URL demandée sera appelée, et elle retournera donc la réponse HTML au contrôleur (qui, lui, la retournera à l'utilisateur). Si aucune URL ne correspond à un schéma que vous avez défini, alors Django renverra une page d'erreur 404. Le schéma d'exécution est celui de la figure suivante.

Schéma d'exécution d'une requête (nous travaillons pour le moment sans templates et sans modèles)
Schéma d'exécution d'une requête (nous travaillons pour le moment sans templates et sans modèles)

Occupons-nous uniquement de la liste urlpatterns, qui permet de définir les associations entre URL et vues. Une association de routage basique se définit par un sous-tuple composé des éléments suivants :

  • Le schéma de l'URL : une URL peut être composée d'arguments qui permettent par la suite de retrouver des informations dans les modèles par exemple. Exemple : un titre d'article, le numéro d'un commentaire, etc. ;

  • Le chemin Python vers la vue correspondante.

Par exemple, en reprenant la vue définie tout à l'heure, si nous souhaitons que celle-ci soit accessible depuis l'URL http://www.crepes-bretonnes.com/accueil, il suffit de rajouter cette règle dans votreurlpatterns:

from django.urls import path
from blog import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accueil', views.home),
]

Comme pour la ligne de l'admin, le premier argument représenter le fragment d'URL auquel le visiteur accédera et le second argument est la vue que l'on a créé dans le fichier blog/views.py . On y accède donc via le module views  importé juste au-dessus.

Grâce à cette règle, Django saura que lorsqu'un client demande la pagehttp://www.crepes-bretonnes.com/accueil, il devra appeler la vueblog.views.home.

Enregistrez les modifications, lancez le serveur de développement Django et laissez-le tourner (pour rappel :python manage.py runserver ), et rendez-vous sur http://localhost:8000/accueil. Vous devriez obtenir quelque chose comme la figure suivante.

L'affichage de votre première vue
L'affichage de votre première vue

Si c'est le cas, félicitations, vous venez de créer votre première vue !

Organiser proprement vos URL

Dans la partie précédente, nous avions parlé de deux avantages importants de Django : la réutilisation d'applications et la structuration du code. Sauf qu'évidemment, un problème se pose avec l'utilisation des URL que nous avons faites : si nous avons plusieurs applications, toutes les URL de celles-ci iraient dansurls.py  du projet, ce qui compliquerait nettement la réutilisation d'une application et ne structure en rien votre code.

En effet, il faudrait sans cesse recopier toutes les URL d'une application en l'incluant dans un projet, et une application complexe peut avoir des centaines d'URL, ce qui ne facilite pas la tâche du développeur. Sans parler de la problématique qui survient lorsqu'il faut retrouver la bonne vue parmi la centaine de vues déjà écrites. C'est pour cela qu'il est généralement bien vu de créer dans chaque application un fichier également nommé urls.py  et d'inclure ce dernier par la suite dans le fichier urls.py  global du projet.

Comment procède-t-on ?

Tout d'abord, il faut créer un fichier urls.py  dans le dossier de votre application, ici blog. Ensuite, il suffit d'y réécrire l'URL que nous avons déjà écrite précédemment (ne pas oublier l'importation des modules nécessaires !) :

from django.urls import path
from . import views

urlpatterns = [
    path('accueil', views.home),
]

Maintenant, retournons à crepes_bretonnes/urls.py. Nous pouvons y enlever la règle réécrite dans blog/urls.py  et la remplacer par une nouvelle qui utilise la fonction include  de django.conf.urls  :

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include('blog.urls')),
]

Ici, on a défini le préfixe d'URLblog/. Cette portion va précéder toutes les URL incluses. Autrement dit, nous avions une URL/accueil  qui envoyait vers la vue blog.views.home, désormais celle-ci sera accessible depuis /blog/accueil. Et cela vaut pour toutes les futures URL importées. Cependant, rien ne vous empêche de laisser cette chaîne de caractères vide (/accueil  restera /accueil  dans ce cas), mais il s'agit d'une bonne solution pour structurer vos URL et éviter les conflits entre vos applications.

Passer des arguments à vos vues

Nous avons vu comment lier des URL à des vues et comment les organiser. Cependant, un besoin va bientôt se faire sentir : pouvoir passer des paramètres dans nos adresses directement. Si vous observez les adresses du site Instagram (qui est basé sur Django pour rappel), le lien vers une photo est construit ainsi :http://instagram.com/p/*******  où *******  est une suite de caractères alphanumériques. Cette suite représente en réalité l'identifiant de la photo sur le site et permet à la vue de récupérer les informations en relation avec cette photo.

Pour passer des arguments dans une URL, il faut capturer ces arguments directement depuis l'écriture de nos URL. Par exemple, si nous souhaitons sur notre blog pouvoir accéder à un certain article via l'adresse/blog/article/**  où **  sera l'identifiant de l'article (un nombre unique), il faut fournir le routage suivant dans votreblog/urls.py:

urlpatterns = [
    path('accueil', views.home),
    path('article/<id_article>', views.view_article),
]

 Lorsque l'URL/blog/article/42est demandée, Django regarde le routage et exécute la fonctionview_article, en passant en paramètre 42. Autrement dit, Django appelle la vue de cette manière :view_article(request, 42). Voici un exemple d'implémentation :

def view_article(request, id_article):
    """ 
    Vue qui affiche un article selon son identifiant (ou ID, ici un numéro)
    Son ID est le second paramètre de la fonction (pour rappel, le premier
    paramètre est TOUJOURS la requête de l'utilisateur)
    """
    return HttpResponse(
        "Vous avez demandé l'article n° {0} !".format(id_article)    
    )

On note que dans ce cas, nous ne validons pas le contenu de  id_article. Cela peut très bien être du texte. Il est possible de capturer plusieurs paramètres, dans une même URL et forcer son type :

urlpatterns = [
    path('accueil', views.home),
    path('article/<id_article>', views.view_article), 
    path('articles/<str:tag>', views.list_articles_by_tag), 
    path('articles/<int:year>/<int:month>', views.list_articles),  
]

Ici, on peut imaginer que l'on a deux autres vues. La première qui prend en paramètre du texte, permettant de filtrer les articles à afficher selon leurs mots-clés et une autre qui affiche les articles par mois. Il existe 5 types de données identifiables par ce système de routage :

  • str  : c'est le format par défaut (celui utilisé pour notre id_article par exemple). Cela permet de récupérer une chaine de caractères non vide, excepté le caractère "/"

  • int  : correspond à une suite de chiffres, et renverra donc un entier à notre vue ;

  • slug  : correspond à une chaine de caractères sans accents ou caractères spéciaux. Un exemple de slug peut être mon-1er-article-de-blog ;

  • uuid  : format standardisé de données, souvent utiliser pour avoir des identifiants uniques.

  • path  : Similaire à str mais accepte également le "/". Cela permet de récupérer n'importe quel URL quelque soit son nombre de segments

Écrire des URL un peu plus avancée

Le format d'URL que l'on a présenté jusqu'à maintenant n'a été introduit que dans Django 2.0 ! Il permet une compréhension plus rapide pour les débutants et couvre une bonne partie des cas d'usages. Cependant, l'ancienne méthode cohabite toujours, qui a une souplesse bien plus grande, mais demande un peu plus de connaissances.

Cette méthode se base sur les expressions régulières. Si nous reprenons l'exemple précédent, nous pouvons l'écrire également de cette façon :

from django.urls import path, re_path
from . import views

urlpatterns = [
    re_path(r'^accueil', views.home),
    re_path(r'^article/(?P<id_article>.+)', views.view_article), 
    re_path(r'^articles/(?P<tag>.+)', views.list_articles_by_tag), 
    re_path(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})', views.list_articles),  
]

On peut voir que cela est plus verbeux mais de la validation peut déjà être fait en amont de la vue avec cette méthode. Si vous regardez attentivement la dernière ligne, vous pouvez décortiquez le schéma d'URL comme ceci :

  • ^articles/

  • (?P<year>\d{4})/

  • (?P<month>\d{2})

Pour que views.list_articles soit appelé, l'URL visité par l'utilisateur doit commencer part  articles/  (d'où le ^ en début de chaine), être suivi d'un nombre à quatre chiffres (  \d{4}  ), que la vue disposera sous le nom "year" et suivi d'un nombre à deux chiffres, qui s’appellera "month" dans la vue.

Si vous n'êtes pas assez à l'aise avec les expressions régulières, vous pouvez n'utilisez que la méthode path que l'on a détaillé plus haut et revenir sur celle-ci selon vos besoins et après avoir acquis plus de compétences.

Un dernier mot sur les paramètres de vue

Quand nous avons écrit nos URL, nous avons pris soin de nommer les paramètres à chaque fois. Cela permet à Django de faire la correspondance entre ce qu'il a décortiqué dans l'URL envoyé par l'utilisateur et ce qui sera envoyé à votre vue. Ainsi, l'ordre des paramètres importe peu dans votre vue (hormis le premier, qui est toujours  request). Ainsi, en reprenant notre configuration plus haut, la vue suivante est tout à fait valable :

def list_articles(request, month, year):
    """ Liste des articles d'un mois précis. """
    return HttpResponse(
        "Vous avez demandé les articles de {0} {1}.".format(month, year)  
    )

Dans cet exemple, mois et année (month  et year) ne sont pas dans le même ordre entre le urls.py  et le views.py, mais Django s'en occupe et règle l'ordre des arguments en fonction des noms qui ont été donnés dans leurls.py. En réalité, le framework va exécuter la fonction de cette manière :

list_articles(request, year=2014, month=9)

Il faut juste s'assurer que les noms de variables donnés dans le fichierurls.py  coïncident avec les noms donnés dans la déclaration de la vue, sans quoi Python retournera une erreur.

Il est également possible d'avoir plusieurs URL qui pointe vers la même vue, via des arguments optionnels.

# urls.py
urlpatterns = [
    path('articles/<int:year>/', views.list_articles),
    path('articles/<int:year>/<int:month>', views.list_articles),
]

# views.py
def list_articles(request, year, month=1):
    return HttpResponse('Articles de %s/%s' % (year, month))

Dans cet exemple, les adresses  /articles/2020/5  et  /articles/2020/  sont valides, cette dernière aura implicitement "1" pour le mois. Par ailleurs,  /articles/2020/  et  /articles/2020/1  renverront le même résultat.

Pour terminer, sachez qu'il est toujours possible de passer des paramètres GET. Par exemple :http://www.crepes-bretonnes.com/blog/article/1337?ref=twitter. Django tentera de trouver le pattern correspondant en ne prenant en compte que ce qui est avant les paramètres GET, c'est-à-dire/blog/article/1337. Les paramètres passés par la méthode GET sont bien évidemment récupérables, via le dictionnaire request.GET  dans la vue. Ici,request.GET['ref'] retournerait'twitter' .

Des réponses spéciales

Jusqu'ici, nous avons vu comment renvoyer une page HTML standard. Cependant, il se peut que nous souhaitions renvoyer autre chose que du HTML : une erreur 404 (page introuvable), une redirection vers une autre page, etc.

Simuler une page non trouvée

Parfois, une URL correspond bien à un pattern mais ne peut tout de même pas être considérée comme une page existante. Par exemple, lorsque vous souhaitez afficher un article avec un identifiant introuvable, il est impossible de renvoyer une page, même si Django a correctement identifié l'URL et utilisé la bonne vue. Dans ce cas-là, nous pouvons le faire savoir à l'utilisateur via une page d'erreur 404, qui correspond au code d'erreur indiquant qu'une page n'a pas été trouvée. Pour ce faire, il faut utiliser une exception du framework :Http404. Cette exception, du module django.http, arrête le traitement de la vue, et renvoie l'utilisateur vers une page d'erreur.

Voici un rapide exemple d'une vue compatible avec une des règles de routage que nous avons décrites dans le sous-chapitre précédent :

from django.http import HttpResponse, Http404

def view_article(request, id_article):
    # Si l'ID est supérieur à 100, nous considérons que l'article n'existe pas
    if id_article > 100:
        raise Http404

    return HttpResponse('<h1>Mon article ici</h1>')

Si à l'appel de la page l'argumentid_article  est supérieur à 100, la page retournée sera une erreur 404 de Django, visible à la figure suivante. Il est bien entendu possible de personnaliser par la suite cette vue, avec un template, afin d'avoir une page d'erreur qui soit en accord avec le design de votre site, mais cela ne fonctionne uniquement qu'avecDEBUG = False  dans le settings.py(en production donc). Si vous êtes en mode de développement, vous aurez toujours une erreur similaire à la figure suivante.

Erreur 404, page introuvable
Erreur 404, page introuvable

Rediriger l'utilisateur

Le second cas que nous allons aborder concerne les redirections. Il arrive que vous souhaitiez rediriger votre utilisateur vers une autre page lorsqu'une action vient de se dérouler, ou en cas d'erreur rencontrée. Par exemple, lorsqu'un utilisateur se connecte, il est souvent redirigé soit vers l'accueil, soit vers sa page d'origine. Une redirection est réalisable avec Django via la méthode redirect  qui renvoie un objetHttpResponseRedirect  (classe héritant deHttpResponse), qui redirigera l'utilisateur vers une autre URL. La méthode redirect  peut prendre en paramètres plusieurs types d'arguments, dont notamment une URL brute (chaîne de caractères) ou le nom d'une vue.

Si par exemple vous voulez que votre vue, après une certaine opération, redirige vos visiteurs vers le site officiel de Django, il faudrait procéder ainsi :

from django.shortcuts import redirect

def list_articles(request, year, month):
    # Il veut des articles ? Soyons fourbe et redirigeons-le vers djangoproject.com
    return redirect("https://www.djangoproject.com")

N'oubliez pas qu'une URL valide pour accéder à cette vue serait/blog/articles/2014/09.

Cependant, si vous souhaitez rediriger votre visiteur vers une autre page de votre site web, il est plus intéressant de privilégier l'autre méthode, qui permet de garder indépendante la configuration des URL et des vues. Nous devons donc passer en argument le nom de la vue vers laquelle nous voulons rediriger l'utilisateur, avec éventuellement des arguments destinés à celle-ci.

from django.http import HttpResponse, Http404
from django.shortcuts import redirect

def view_article(request, id_article):
    if id_article > 100:
        raise Http404

    return redirect(view_redirection)

def view_redirection(request):
    return HttpResponse("Vous avez été redirigé.")
path('redirection', views.view_redirection),

Ici, si l'utilisateur accède à l'URL/blog/article/101, il aura toujours une page 404. Par contre, s'il choisit un ID inférieur à 100, alors il sera redirigé vers la seconde vue, qui affiche un simple message.

Il est également possible de préciser si la redirection est temporaire ou définitive en ajoutant le paramètrepermanent=True. L'utilisateur ne verra aucune différence, mais ce sont des détails que les moteurs de recherche prennent en compte lors du référencement de votre site web.

Si nous souhaitions rediriger un visiteur vers la vueview_article  définie précédemment par un ID d'article spécifique, il suffirait simplement d'utiliser la méthoderedirect  ainsi :

return redirect(view_article, id_article=42)

Finalement, il est également possible d'indiquer une vue à redirect à l'aide d'une nouvelle manière : en indiquant le nom de la vue tel que renseigné dans urls.py

En réalité, la fonctionredirect  agit en deux temps. Elle va tout d'abord construire l'URL vers la vue selon le routage indiqué dansurls.py. Ici, elle va donc générer l'URL/blog/article/42et ensuite rediriger l'utilisateur vers cette URL. Ainsi, si par la suite vous souhaitez modifier vos URL dansurls.py, toutes les redirections se mettront à jour automatiquement. Il s'agit d'une fonctionnalité extrêmement pratique, il ne faut donc jamais écrire d'URL en dur dans vos vues, sauf quand cette méthode est inutilisable (vers des sites tiers par exemple).

Sachez qu'au lieu d'écrire à chaque fois tout le chemin d'une vue ou de l'importer, il est possible de lui assigner un nom plus court et plus facile à utiliser dansurls.py. Par exemple :

path('article/<int:id_article>$', views.view_article, name='afficher_article'),

Notez le paramètre name='afficher_article'  qui permet d'indiquer le nom de la vue. Avec ce routage, en plus de pouvoir passer directement la fonction ou le chemin vers celle-ci en argument, nous pouvons faire beaucoup plus court et procéder comme ceci :

return redirect('afficher_article', id_article=42)

Pour terminer, sachez qu'il existe également une fonction qui permet de générer simplement l'URL et s'utilise de la même façon queredirect; il s'agit dereverse(django.urls.reverse). Cette fonction ne retournera pas un objetHttpResponseRedirect, mais simplement une chaîne de caractères contenant l'URL vers la vue selon les éventuels arguments donnés. Une variante de cette fonction sera utilisée dans les templates peu après pour générer des liens HTML vers les autres pages du site.

En résumé

  • Le minimum requis pour obtenir une page web avec Django est une vue, associée à une URL.

  • Une vue est une fonction placée dans le fichier views.py  d'une application. Cette fonction doit toujours renvoyer un objet HttpResponse.

  • Pour être accessible, une vue doit être liée à une ou plusieurs URL dans les fichiers urls.py  du projet.

  • Les URL sont désignées par des schémas ou expressions régulières, permettant la gestion d'arguments qui peuvent être passés à la vue pour rendre l'affichage différent selon l'URL visitée.

  • Il est conseillé de diviser le urls.py  du projet en plusieurs fichiers, en créant un fichierurls.py  par application.

  • Il existe des réponses plus spéciales permettant d'envoyer au navigateur du client les codes d'erreur 404 (page non trouvée) et 403 (accès refusé), ou encore d'effectuer des redirections.

Example of certificate of achievement
Example of certificate of achievement