|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- title: Rédaction de votre première appli Django, partie 4 : Conception d'un formulaire et vues génériques
- slug: redaction-de-votre-premiere-appli-django-partie-4-conception-d-un-formulaire-et-vues-generiques
- date: 2006-06-17 19:50:21
- type: post
- vignette: images/logos/django.png
- contextual_title1: Le langage de template Django : Pour les auteurs de templates
- contextual_url1: 20060815-le-langage-de-template-django-pour-les-auteurs-de-templates
- contextual_title2: Comparaison de TurboGears et Django, deux frameworks web Python
- contextual_url2: 20060715-comparaison-de-turbogears-et-django-deux-frameworks-web-python
- contextual_title3: Rédaction de votre première appli Django, partie 3 : Création des vues de l'interface publique
- contextual_url3: 20060617-redaction-de-votre-premiere-appli-django-partie-3-creation-des-vues-de-l-interface-publique
-
- <p>Ce tutoriel commence là où le <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-3-creation-des-vues-de-l-interface-publique/">Tutoriel 3</a> s'est achevé. Nous continuons notre
- application de sondage en ligne et allons nous intéresser à la génération d'un
- formulaire simple et au ré-arrangement de notre code.</p>
-
- <div class="section">
- <h1><a id="g-n-rer-un-formulaire-simple" name="g-n-rer-un-formulaire-simple">Générer un formulaire simple</a></h1>
- <p>Commençons par mettre à jour notre template de détail de sondage issu du dernier
- tutoriel de façon à ce que le template contienne un élement HTML <tt class="docutils literal"><span class="pre"><form></span></tt>:</p>
- <pre class="literal-block">
- <h1>{{ poll.question }}</h1>
-
- {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
-
-
- <form action="/polls/{{ poll.id }}/vote/" method="post">
- {% for choice in poll.choice_set.all %}
- <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
-
- <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
- {% endfor %}
- <input type="submit" value="Voter" />
- </form>
-
- </pre>
- <p>Un bref rappel :</p>
- <blockquote>
- <ul class="simple">
- <li>Le template ci-dessus affiche un bouton radio pour chaque choix du sondage.
- La valeur <tt class="docutils literal"><span class="pre">value</span></tt> de chaque bouton radio est associée à l'ID de chaque choix.
- Le nom <tt class="docutils literal"><span class="pre">name</span></tt> de chaque bouton radio est <tt class="docutils literal"><span class="pre">"choice"</span></tt>. Cela signifie que
- lorsque quelqu'un sélectionne l'un des boutons radio et soumet
- le formulaire, il envoie la donnée de type POST <tt class="docutils literal"><span class="pre">choice=3</span></tt>. C'est un
- formulaire HTML 101.</li>
-
- <li>Nous avons assigné l'<tt class="docutils literal"><span class="pre">action</span></tt> du formulaire à
- <tt class="docutils literal"><span class="pre">/polls/{{</span> <span class="pre">poll.id</span> <span class="pre">}}/vote/</span></tt>, et nous avons mis <tt class="docutils literal"><span class="pre">method="post"</span></tt>.
- L'utilisation de <tt class="docutils literal"><span class="pre">method="post"</span></tt> (au contraire de <tt class="docutils literal"><span class="pre">method="get"</span></tt>) est
- très importante, car le fait de soumettre ce formulaire va être à l'origine
- d'une modification des données du côté du serveur. À chaque fois que vous
- créez un formulaire qui modifie des données sur le serveur, utilisez
-
- <tt class="docutils literal"><span class="pre">method="post"</span></tt>. Cet adage n'est pas spécifique à Django, ce sont juste
- de bonnes pratiques de développement web.</li>
- </ul>
- </blockquote>
- <p>À présent, créons une vue Django qui récupère les données soumises et fait
- quelque chose avec. Rappelez-vous, dans le <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-3-creation-des-vues-de-l-interface-publique/">Tutoriel 3</a>, nous avons créé une
- URLconf pour l'application de sondage qui incluait cette ligne:</p>
- <pre class="literal-block">
- (r'^(?P<poll_id>\d+)/vote/$', 'monsite.polls.views.vote'),
- </pre>
- <p>Donc créons une fonction <tt class="docutils literal"><span class="pre">vote()</span></tt> dans <tt class="docutils literal"><span class="pre">monsite/polls/views.py</span></tt>:</p>
-
- <pre class="literal-block">
- from django.shortcuts import get_object_or_404, render_to_response
- from django.http import HttpResponseRedirect
- from monsite.polls.models import Choice, Poll
- # ...
- def vote(request, poll_id):
- p = get_object_or_404(Poll, pk=poll_id)
- try:
- selected_choice = p.choice_set.get(pk=request.POST['choice'])
- except (KeyError, Choice.DoesNotExist):
- # Réaffiche le formulaire de vote.
- return render_to_response('polls/detail.html', {
- 'poll': p,
- 'error_message': "Vous n'avez pas sélectionné de choix.",
- })
- else:
- selected_choice.votes += 1
- selected_choice.save()
- # Retourne toujours un HttpResponseRedirect après validation
- # des données POST. Ceci empèche que les données soient postées
- # deux fois si l'utilisateur clique sur le bouton Précédent.
- return HttpResponseRedirect('/polls/%s/results/' % p.id)
- </pre>
- <p>Ce code inclus des parties que nous n'avons pas encore étudiées dans ce tutoriel :</p>
- <blockquote>
- <ul>
- <li><p class="first"><tt class="docutils literal"><span class="pre">request.POST</span></tt> est un objet de type dictionnaire qui vous laisse accéder
- aux données soumises avec les mots-clé. Dans notre cas,
- <tt class="docutils literal"><span class="pre">request.POST['choice']</span></tt> retourne l'ID du choix sélectionné, sous forme
- de chaîne de caractères. Les valeurs issues de <tt class="docutils literal"><span class="pre">request.POST</span></tt> sont
- toujours des chaînes de caractères.</p>
-
- <p>Notez que Django vous permet aussi d'accéder via <tt class="docutils literal"><span class="pre">request.GET</span></tt> aux
- données de type GET de la même façon -- mais nous utilisons explicitement
- <tt class="docutils literal"><span class="pre">request.POST</span></tt> dans notre code, pour nous assurer que la modification des
- données se fasse uniquement par un appel à POST.</p>
- </li>
- <li><p class="first"><tt class="docutils literal"><span class="pre">request.POST['choice']</span></tt> déclenchera une <tt class="docutils literal"><span class="pre">KeyError</span></tt> si <tt class="docutils literal"><span class="pre">choice</span></tt> n'a pas
- été obtenu par des données de type POST. Le code ci-dessus vérifie les
-
- <tt class="docutils literal"><span class="pre">KeyError</span></tt> et réaffiche le formulaire de sondage avec un message
- d'erreur si <tt class="docutils literal"><span class="pre">choice</span></tt> n'a pas été donné.</p>
- </li>
- <li><p class="first">Après avoir incrémenté le compteur associé au choix soumis, le script
- retourne une <tt class="docutils literal"><span class="pre">HttpResponseRedirect</span></tt> à la place du <tt class="docutils literal"><span class="pre">HttpResponse</span></tt>
- habituel. <tt class="docutils literal"><span class="pre">HttpResponseRedirect</span></tt> prend un seul argument : l'URL
- vers laquelle l'utilisateur va être redirigé. Vous pouvez omettre de
- mentionner le « <a class="reference" href="http://">http://</a> » et le nom de domaine quand cela est possible.
- Cela permet à votre appli d'être portable sur différents domaines.</p>
-
- <p>Comme le commentaire Python le souligne, vous devez toujours retourner une
- <tt class="docutils literal"><span class="pre">HttpResponseRedirect</span></tt> après avoir traité une donnée de type POST avec
- succès. Cet adage n'est pas spécifique à Django, c'est juste une bonne
- pratique de développement web.</p>
- </li>
- </ul>
- </blockquote>
- <p>Comme il est mentionné dans le Tutoriel 3, <tt class="docutils literal"><span class="pre">request</span></tt> est un objet
- <tt class="docutils literal"><span class="pre">HTTPRequest</span></tt>. Pour en savoir plus au sujet des objets <tt class="docutils literal"><span class="pre">HTTPRequest</span></tt>, lisez
- la <a class="reference" href="http://www.djangoproject.com/documentation/request_response/">documentation sur les requêtes et réponses</a>.</p>
-
- <p>Après qu'un vote ait été effectué dans un sondage, la vue <tt class="docutils literal"><span class="pre">vote()</span></tt> redirige
- vers la page de résultats du sondage. Écrivons cette vue:</p>
- <pre class="literal-block">
- def results(request, poll_id):
- p = get_object_or_404(Poll, pk=poll_id)
- return render_to_response('polls/results.html', {'poll': p})
- </pre>
- <p>C'est presque la même que la vue <tt class="docutils literal"><span class="pre">detail()</span></tt> du <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-3-creation-des-vues-de-l-interface-publique/">Tutoriel 3</a>. La seule
- différence est le nom du template. Nous nous occuperons de la redondance plus
- tard.</p>
- <p>Maintenant, créons un template <tt class="docutils literal"><span class="pre">results.html</span></tt>:</p>
-
- <pre class="literal-block">
- <h1>{{ poll.question }}</h1>
-
- <ul>
- {% for choice in poll.choice_set.all %}
- <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
- {% endfor %}
- </ul>
- </pre>
-
- <p>À présent, rendez vous à l'adresse <tt class="docutils literal"><span class="pre">/polls/1/</span></tt> dans votre navigateur et
- soumettez un vote. Vous devriez voir une page de résultats qui est mise à
- jour à chacun de vos votes. Si vous soumettez le formulaire sans avoir spécifié
- de choix, vous devriez observer le message d'erreur.</p>
- </div>
- <div class="section">
- <h1><a id="utilisez-les-vues-g-n-riques-moins-il-y-a-de-code-mieux-c-est" name="utilisez-les-vues-g-n-riques-moins-il-y-a-de-code-mieux-c-est">Utilisez les vues génériques : Moins il y a de code mieux c'est</a></h1>
- <p>Les vues <tt class="docutils literal"><span class="pre">detail()</span></tt> (du <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-3-creation-des-vues-de-l-interface-publique/">Tutoriel 3</a>) et <tt class="docutils literal"><span class="pre">results()</span></tt> sont ridiculeusement
- simples -- et, comme nous l'avons signalé plus haut, redondantes. La vue
-
- <tt class="docutils literal"><span class="pre">index()</span></tt> (du <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-3-creation-des-vues-de-l-interface-publique/">Tutoriel 3</a> aussi), qui affiche une liste de sondages, est
- similaire.</p>
- <p>Ces vues correspondent à des cas courants de développement Web basic : récupérer
- des données de la base de données en fonction d'un paramètre passé dans l'URL,
- charger un template et retourner le template complété. Comme c'est si courant,
- Django dispose d'un raccourci, baptisé le système des « vues génériques ».</p>
- <p>Les vues génériques fournissent une couche d'abstraction tellement importante que
- vous n'avez même pas besoin de rédiger du code Python pour écrire votre appli.</p>
- <p>Convertissons notre application de sondage en utilisant le système des vues
- génériques, nous allons ainsi pouvoir supprimer une bonne partie du code que
- nous avons écrit précédemment. Il suffit de quelques étapes pour réaliser la
- conversion.</p>
- <div class="admonition-pourquoi-ce-r-arrangement-du-code admonition">
- <p class="first admonition-title">Pourquoi ce ré-arrangement du code ?</p>
- <p class="last">Géneralement, quand vous allez écrire une application Django, vous allez
- évaluer si les vues génériques sont une bonne solution à votre problème, et
- vous allez les utiliser dès le début sans avoir à refactoriser votre code en
- plein développement. Mais ce tutoriel s'est intentionnellement focalisé sur
- l'écriture de vues « de manière hardue » jusqu'à présent, de façon à ce que
- vous preniez conscience des concepts généraux.</p>
-
- </div>
- <p>Premièrement, éditez votre fichier d'URLconf polls/urls.py. Il
- devrait ressembler à ça, d'après les tutoriaux précédents:</p>
- <pre class="literal-block">
- from django.conf.urls.defaults import *
-
- urlpatterns = patterns('monsite.polls.views',
- (r'^$', 'index'),
- (r'^(?P<poll_id>\d+)/$', 'detail'),
- (r'^(?P<poll_id>\d+)/results/$', 'results'),
- (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
- )
- </pre>
- <p>Faites le ressembler à:</p>
- <pre class="literal-block">
- from django.conf.urls.defaults import *
- from monsite.polls.models import Poll
-
- info_dict = {
- 'queryset': Poll.objects.all(),
- }
-
- urlpatterns = patterns('',
- (r'^$', 'django.views.generic.list_detail.object_list', info_dict),
- (r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
- (r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html')),
- (r'^(?P<poll_id>\d+)/vote/$', 'monsite.polls.views.vote'),
- )
-
- </pre>
- <p>Nous utilisons ici deux vues génériques : <tt class="docutils literal"><span class="pre">object_list</span></tt> et <tt class="docutils literal"><span class="pre">object_detail</span></tt>.
- Respectivement, ces deux vues rendent abstraits les concepts « d'affichage d'une
- liste d'objets » et « d'affichage d'une page de détails pour un type particulier
- d'objet ».</p>
- <blockquote>
- <ul class="simple">
- <li>Chaque vue générique a besoin de connaître sur quelles données est-ce
- qu'elle va agir. Ces données sont fournies grâce à un dictionnaire. La
- clé <tt class="docutils literal"><span class="pre">queryset</span></tt> de ce dictionnaire pointe sur la liste d'objets qui doit
- être manipulée par la vue générique.</li>
- <li>La vue générique <tt class="docutils literal"><span class="pre">object_detail</span></tt> attend la valeur ID issue de l'URL qui
- doit être appelée <tt class="docutils literal"><span class="pre">"object_id"</span></tt>, nous avons donc changé <tt class="docutils literal"><span class="pre">poll_id</span></tt> en
-
- <tt class="docutils literal"><span class="pre">object_id</span></tt> pour la vue générique.</li>
- </ul>
- </blockquote>
- <p>Par défaut, la vue générique <tt class="docutils literal"><span class="pre">object_detail</span></tt> utilise un template appelé
- <tt class="docutils literal"><span class="pre"><nom_appli>/<nom_module>_detail.html</span></tt>. Dans notre cas, ce sera le template
- appelé <tt class="docutils literal"><span class="pre">"polls/poll_detail.html"</span></tt>. Donc, renommons <tt class="docutils literal"><span class="pre">polls/detail.html</span></tt> en
-
- <tt class="docutils literal"><span class="pre">polls/poll_detail.html</span></tt>, et changeons la ligne <tt class="docutils literal"><span class="pre">render_to_response()</span></tt> en
- <tt class="docutils literal"><span class="pre">vote()</span></tt>.</p>
- <p>De façon similaire, la vue générique <tt class="docutils literal"><span class="pre">object_list</span></tt> utilise un template appelé
- <tt class="docutils literal"><span class="pre"><nom_appli>/<nom_module>_list.html</span></tt>. Donc, renommons <tt class="docutils literal"><span class="pre">polls/index.html</span></tt> en
-
- <tt class="docutils literal"><span class="pre">polls/poll_list.html</span></tt>.</p>
- <p>Puisque nous avons plus d'une entrée dans l'URLconf qui
- utilisent <tt class="docutils literal"><span class="pre">object_detail</span></tt> pour l'application de sondage, nous spécifions
- manuellement un nom de template pour la vue des résultats :
- <tt class="docutils literal"><span class="pre">template_name='polls/results.html'</span></tt>. Sinon, les deux vues auraient utilisé le
- même template. Notez l'utilisation de <tt class="docutils literal"><span class="pre">dict()</span></tt> pour retourner un dictionnaire
- modifié à la place.</p>
- <p>Dans les tutoriels précédents, les templates sont utilisés dans un contexte
- contenant les variables <tt class="docutils literal"><span class="pre">poll</span></tt> et <tt class="docutils literal"><span class="pre">latest_poll_list</span></tt>. Ici, les vues
- génériques nécessitent d'avoir les variables contextuelles <tt class="docutils literal"><span class="pre">object</span></tt> et
-
- <tt class="docutils literal"><span class="pre">object_list</span></tt>. Éditez vos templates et modifiez chaque référence à
- <tt class="docutils literal"><span class="pre">latest_poll_list</span></tt> par <tt class="docutils literal"><span class="pre">object_list</span></tt>, et chaque référence à <tt class="docutils literal"><span class="pre">poll</span></tt> par
- <tt class="docutils literal"><span class="pre">object</span></tt>.</p>
- <p>Vous pouvez maintenant supprimer les vues <tt class="docutils literal"><span class="pre">index()</span></tt>, <tt class="docutils literal"><span class="pre">detail()</span></tt> et
-
- <tt class="docutils literal"><span class="pre">results()</span></tt> de <tt class="docutils literal"><span class="pre">polls/views.py</span></tt>. Nous n'en avons plus besoin -- elles ont
- été remplacées par les vues génériques.</p>
- <p>La vue <tt class="docutils literal"><span class="pre">vote()</span></tt> est toujours requise. Néanmoins, elle doit être modifiée pour
- être utilisée dans les nouveaux templates et les nouvelles variables
- contextuelles.
- Changez l'appel au template <tt class="docutils literal"><span class="pre">polls/detail.html</span></tt> par <tt class="docutils literal"><span class="pre">polls/poll_detail.html</span></tt>,
- et passez <tt class="docutils literal"><span class="pre">object</span></tt> dans le contexte au lieu de <tt class="docutils literal"><span class="pre">poll</span></tt>.</p>
-
- <p>Lancez le serveur et utilisez votre nouvelle appli de sondage avec les vues
- génériques.</p>
- <p>Pour davantage de détails quant aux vues génériques, lisez la <a class="reference" href="http://www.djangoproject.com/documentation/generic_views/">documentation
- sur les vues génériques</a>.</p>
- </div>
- <div class="section">
- <h1><a id="venir" name="venir">À venir</a></h1>
- <p>Les tutoriels s'arrêtent ici pour le moment. Mais revenez prochainement pour la
- suite des réjouissances :</p>
- <blockquote>
- <ul class="simple">
- <li>Utilisation avancée des formulaires</li>
- <li>Utilisation du framework RSS</li>
-
- <li>Utilisation du framework de cache</li>
- <li>Utilisation du framework de commentaires</li>
- <li>Fonctionnalités avancées d'administration : Permissions</li>
- <li>Fonctionnalités avancées d'administration : Javascript</li>
- </ul>
- </blockquote>
- <p>Vous pouvez maintenant retourner à la <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-traduction-francaise-de-la-documentation-de-django-le-framework-web-python/">page d'accueil des traductions de la
- documentation de Django</a>.</p>
- <p>Cette traduction correspond à la révision 3589 (post 0.95).</p>
- </div>
-
|