Introduction
Ce tutoriel reprend là où Configurez la base de données s'est arrêté. Nous continuons l'application de sondage web et nous allons nous concentrer sur la création de l'interface publique - les "vues".
💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici
Ce tutoriel reprend là où Configurez la base de données s'est arrêté. Nous continuons l'application de sondage web et nous allons nous concentrer sur la création de l'interface publique - les "vues".
Une vue est un "type" de page web dans votre application Django qui a généralement une fonction spécifique et un modèle spécifique. Par exemple, dans une application de blog, vous pourriez avoir les vues suivantes :
Dans notre application de sondage, nous aurons les quatre vues suivantes :
Dans Django, les pages web et le reste du contenu sont fournis par les vues. Chaque vue est représentée par une fonction Python (ou une méthode, dans le cas des vues basées sur des classes). Django choisira une vue en examinant l'URL demandée (plus précisément, la partie de l'URL après le nom de domaine).
Maintenant, au fil de vos expériences sur le web, vous avez peut-être croisé des beautés telles que ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B
. Vous serez ravis de savoir que Django nous permet des modèles d'URL bien plus élégants que ça.
Un modèle d'URL est la forme générale d'une URL - par exemple : /newsarchive/<year>/<month>/
.
Pour passer d'une URL à une vue, Django utilise ce qu'on appelle des 'URLconf'. Un URLconf associe les modèles d'URL aux vues.
Ce tutoriel fournit des instructions de base sur l'utilisation des URLconf, et vous pouvez vous référer à /topics/http/urls
pour plus d'informations.
Maintenant, ajoutons quelques vues supplémentaires à polls/views.py
. Ces vues sont légèrement différentes, car elles prennent un argument :
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
Reliez ces nouvelles vues au module polls.urls
en ajoutant les appels suivants à ~django.urls.path
:
Modifiez le fichier polls/urls.py
et ajoutez les lignes suivantes :
from django.urls import path
from. import views
urlpatterns = [
## ex: /polls/
path("", views.index, name="index"),
## ex: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
## ex: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
## ex: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]
Maintenant, exécutez le serveur à nouveau :
cd ~/project/mysite
python manage.py runserver 0.0.0.0:8080
Accédez à l'onglet Web 8080, à /polls/34/
. Cela exécutera la méthode detail()
et affichera l'ID que vous fournissez dans l'URL. Essayez également /polls/34/results/
et /polls/34/vote/
- ces derniers afficheront les pages de résultats et de vote de remplacement.
Lorsque quelqu'un demande une page de votre site web - disons, /polls/34/
, Django chargera le module Python mysite.urls
car il est indiqué par la configuration ROOT_URLCONF
. Il trouve la variable nommée urlpatterns
et parcourt les motifs dans l'ordre. Après avoir trouvé la correspondance à 'polls/'
, il enlève le texte correspondant ("polls/"
) et envoie le texte restant - "34/"
- au fichier de configuration d'URL 'polls.urls'
pour traitement supplémentaire. Là, il correspond à '<int:question_id>/'
, ce qui résulte dans un appel à la vue detail()
comme suit :
detail(request=<HttpRequest object>, question_id=34)
La partie question_id=34
vient de <int:question_id>
. En utilisant des crochets, on "captue" une partie de l'URL et on l'envoie en tant qu'argument clé-valeur à la fonction de vue. La partie question_id
de la chaîne définit le nom qui sera utilisé pour identifier le motif correspondant, et la partie int
est un convertisseur qui détermine quels motifs devraient correspondre à cette partie du chemin d'URL. Le deux-points (:
) sépare le convertisseur et le nom du motif.
Chaque vue est responsable de faire l'une des deux choses suivantes : renvoyer un objet ~django.http.HttpResponse
contenant le contenu de la page demandée, ou lever une exception telle que ~django.http.Http404
. Le reste est à vous.
Votre vue peut lire des enregistrements dans une base de données, ou pas. Elle peut utiliser un système de gabarit tel que celui de Django - ou un système de gabarit Python tiers - ou pas. Elle peut générer un fichier PDF, produire un XML, créer un fichier ZIP sur le vol, tout ce que vous voulez, en utilisant les bibliothèques Python que vous voulez.
Tout ce que Django veut, c'est cet objet ~django.http.HttpResponse
. Ou une exception.
Par commodité, utilisons l'API de base de données de Django, que nous avons abordée dans le tutoriel 2. Voici une tentative pour une nouvelle vue index()
, qui affiche les 5 dernières questions de sondage dans le système, séparées par des virgules, selon la date de publication :
Modifiez le fichier polls/views.py
et changez-le pour qu'il ressemble à ceci :
from django.http import HttpResponse
from.models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
output = ", ".join([q.question_text for q in latest_question_list])
return HttpResponse(output)
## Laissez les autres vues (detail, results, vote) inchangées
Il y a cependant un problème ici : la conception de la page est codée en dur dans la vue. Si vous voulez changer la façon dont la page se présente, vous devrez modifier ce code Python. Alors, utilisons le système de gabarit de Django pour séparer la conception du Python en créant un gabarit que la vue peut utiliser.
Tout d'abord, créez un répertoire appelé templates
dans votre répertoire polls
. Django cherchera les gabarits là-dedans.
La configuration TEMPLATES
de votre projet décrit la manière dont Django chargera et affichera les gabarits. Le fichier de configuration par défaut configure un backend DjangoTemplates
dont l'option APP_DIRS <TEMPLATES-APP_DIRS>
est définie sur True
. Par convention, DjangoTemplates
cherche un sous-répertoire "templates" dans chacun des INSTALLED_APPS
.
Dans le répertoire templates
que vous venez de créer, créez un autre répertoire appelé polls
, et à l'intérieur de celui-ci, créez un fichier appelé index.html
. En d'autres termes, votre gabarit devrait se trouver à polls/templates/polls/index.html
. En raison de la façon dont le chargeur de gabarits app_directories
fonctionne comme décrit ci-dessus, vous pouvez vous référer à ce gabarit dans Django sous le nom polls/index.html
.
Maintenant, nous pourrions peut-être nous en sortir en mettant nos gabarits directement dans polls/templates
(au lieu de créer un autre sous-répertoire polls
), mais ce serait en fait une mauvaise idée. Django choisira le premier gabarit qu'il trouvera dont le nom correspond, et si vous aviez un gabarit avec le même nom dans une autre application, Django ne serait pas capable de les distinguer.
Nous devons être en mesure de pointer Django vers le bon gabarit, et le meilleur moyen de s'assurer de cela est en les espacant. C'est-à-dire en mettant ces gabarits à l'intérieur d'un autre répertoire nommé pour l'application elle-même.
Placez le code suivant dans ce gabarit :
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
Remarque :
Pour que le tutoriel soit plus court, tous les exemples de gabarit utilisent un HTML incomplet. Dans vos propres projets, vous devriez utiliser des documents HTML complets.
Maintenant, mettons à jour notre vue index
dans polls/views.py
pour utiliser le gabarit :
from django.http import HttpResponse
from django.template import loader
from.models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
template = loader.get_template("polls/index.html")
context = {
"latest_question_list": latest_question_list,
}
return HttpResponse(template.render(context, request))
Ce code charge le gabarit appelé polls/index.html
et lui passe un contexte. Le contexte est un dictionnaire qui associe les noms de variables de gabarit à des objets Python.
Exécutez le serveur à nouveau :
python manage.py runserver 0.0.0.0:8080
Chargez la page en pointant votre navigateur vers "/polls/", et vous devriez voir une liste à puces contenant la question "What's up" du tutoriel 2. Le lien pointe vers la page de détail de la question.
~django.shortcuts.render
C'est un usage très courant de charger un gabarit, de remplir un contexte et de renvoyer un objet ~django.http.HttpResponse
avec le résultat du gabarit affiché. Django propose un raccourci. Voici la vue complète index()
, réécrite :
from django.shortcuts import render
from.models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
context = {"latest_question_list": latest_question_list}
return render(request, "polls/index.html", context)
Notez qu'une fois que nous l'avons fait dans toutes ces vues, nous n'avons plus besoin d'importer ~django.template.loader
et ~django.http.HttpResponse
(vous voudrez conserver HttpResponse
si vous avez encore les méthodes d'esquisses pour detail
, results
et vote
).
La fonction ~django.shortcuts.render
prend l'objet de requête comme premier argument, le nom du gabarit comme deuxième argument et un dictionnaire comme troisième argument optionnel. Elle renvoie un objet ~django.http.HttpResponse
du gabarit donné affiché avec le contexte donné.
Maintenant, abordons la vue de détail des questions - la page qui affiche le texte de la question pour un sondage donné. Voici la vue :
from django.http import Http404
from django.shortcuts import render
from.models import Question
#...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, "polls/detail.html", {"question": question})
Le nouveau concept ici : La vue lève l'exception ~django.http.Http404
si une question avec l'ID demandé n'existe pas.
Nous discuterons de ce que vous pourriez mettre dans le gabarit polls/detail.html
un peu plus tard, mais si vous voulez rapidement faire fonctionner l'exemple ci-dessus, un fichier contenant seulement :
{{ question }}
vous permettra de commencer pour l'instant.
~django.shortcuts.get_object_or_404
Il est très courant d'utiliser ~django.db.models.query.QuerySet.get
et de lever ~django.http.Http404
si l'objet n'existe pas. Django propose un raccourci. Voici la vue detail()
, réécrite :
from django.shortcuts import get_object_or_404, render
from.models import Question
#...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, "polls/detail.html", {"question": question})
La fonction ~django.shortcuts.get_object_or_404
prend un modèle Django comme premier argument et un nombre arbitraire d'arguments clés, qu'elle passe à la fonction ~django.db.models.query.QuerySet.get
du gestionnaire du modèle. Elle lève ~django.http.Http404
si l'objet n'existe pas.
Pourquoi utilisons-nous une fonction d'aide ~django.shortcuts.get_object_or_404
au lieu de capturer automatiquement les exceptions ~django.core.exceptions.ObjectDoesNotExist
à un niveau supérieur, ou de faire en sorte que l'API du modèle lève ~django.http.Http404
au lieu de ~django.core.exceptions.ObjectDoesNotExist
?
Parce que cela couplerait la couche modèle à la couche vue. L'un des objectifs de conception principaux de Django est de maintenir un faible couplage. Un certain couplage contrôlé est introduit dans le module django.shortcuts
.
Il existe également une fonction ~django.shortcuts.get_list_or_404
, qui fonctionne comme ~django.shortcuts.get_object_or_404
- sauf qu'elle utilise ~django.db.models.query.QuerySet.filter
au lieu de ~django.db.models.query.QuerySet.get
. Elle lève ~django.http.Http404
si la liste est vide.
Revenons à la vue detail()
de notre application de sondage. Étant donné la variable de contexte question
, voici à quoi pourrait ressembler le gabarit polls/detail.html
:
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
Le système de gabarits utilise une syntaxe de recherche par point pour accéder aux attributs des variables. Dans l'exemple de {{ question.question_text }}
, d'abord Django effectue une recherche dans un dictionnaire sur l'objet question
. Si cela échoue, il essaie une recherche d'attribut - ce qui fonctionne, dans ce cas. Si la recherche d'attribut avait échoué, il aurait essayé une recherche par indice de liste.
L'appel de méthode se produit dans la boucle {% for %}<for>
: question.choice_set.all
est interprété comme le code Python question.choice_set.all()
, qui renvoie un itérable d'objets Choice
et est approprié pour être utilisé dans la balise {% for %}<for>
.
Souvenez-vous, lorsque nous avons écrit le lien vers une question dans le gabarit polls/index.html
, le lien était partiellement codé en dur comme ceci :
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
Le problème de cette approche codée en dur et fortement couplée est qu'il devient difficile de changer les URLs dans des projets avec beaucoup de gabarits. Cependant, puisque vous avez défini l'argument name
dans les fonctions ~django.urls.path
du module polls.urls
, vous pouvez éliminer la dépendance sur les chemins d'URL spécifiques définis dans vos configurations d'URL en utilisant la balise de gabarit {% url %}
:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
Le fonctionnement de cela consiste à rechercher la définition d'URL telle que spécifiée dans le module polls.urls
. Vous pouvez voir exactement où le nom d'URL 'detail' est défini ci-dessous :
## la valeur 'name' appelée par la balise de gabarit {% url %}
path("<int:question_id>/", views.detail, name="detail"),
Si vous voulez changer l'URL de la vue de détail des sondages en quelque chose d'autre, peut-être en polls/specifics/12/
au lieu de le faire dans le gabarit (ou les gabarits), vous le changeriez dans polls/urls.py
:
Vous n'avez pas besoin de changer le gabarit du tout.
## ajout du mot'specifics'
path("specifics/<int:question_id>/", views.detail, name="detail"),
Le projet de tutoriel n'a qu'une seule application, polls
. Dans de vrais projets Django, il peut y avoir cinq, dix, vingt applications ou plus. Comment Django distingue-t-il les noms d'URL entre elles? Par exemple, l'application polls
a une vue detail
, et une application du même projet pour un blog pourrait également en avoir une. Comment peut-on s'assurer que Django sait quelle vue d'application créer pour une URL lorsqu'on utilise la balise de gabarit {% url %}
?
La réponse est d'ajouter des espaces de noms à votre configuration d'URL. Dans le fichier polls/urls.py
, allez-y et ajoutez un app_name
pour définir l'espace de noms de l'application :
from django.urls import path
from. import views
app_name = "polls"
urlpatterns = [
## ex: /polls/
path("", views.index, name="index"),
## ex: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
## ex: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
## ex: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]
Maintenant, changez votre gabarit polls/index.html
de :
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
pour pointer vers la vue détaillée avec espace de noms :
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
Lorsque vous serez à l'aise pour écrire des vues, lisez Traitement des formulaires et réduction de notre code pour apprendre les bases sur le traitement des formulaires et les vues génériques.
Félicitations! Vous avez terminé le laboratoire Création des vues de l'interface publique. Vous pouvez pratiquer d'autres laboratoires dans LabEx pour améliorer vos compétences.