title:      Rédaction de votre première appli Django, partie 4 : Conception d&#39;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&#39;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">&lt;form&gt;</span></tt>:</p>
<pre class="literal-block">
&lt;h1&gt;{{ poll.question }}&lt;/h1&gt;

{% if error_message %}&lt;p&gt;&lt;strong&gt;{{ error_message }}&lt;/strong&gt;&lt;/p&gt;{% endif %}


&lt;form action=&quot;/polls/{{ poll.id }}/vote/&quot; method=&quot;post&quot;&gt;
{% for choice in poll.choice_set.all %}
    &lt;input type=&quot;radio&quot; name=&quot;choice&quot; id=&quot;choice{{ forloop.counter }}&quot; value=&quot;{{ choice.id }}&quot; /&gt;

    &lt;label for=&quot;choice{{ forloop.counter }}&quot;&gt;{{ choice.choice }}&lt;/label&gt;&lt;br /&gt;
{% endfor %}
&lt;input type=&quot;submit&quot; value=&quot;Voter&quot; /&gt;
&lt;/form&gt;

</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">&quot;choice&quot;</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=&quot;post&quot;</span></tt>.
L'utilisation de <tt class="docutils literal"><span class="pre">method=&quot;post&quot;</span></tt> (au contraire de <tt class="docutils literal"><span class="pre">method=&quot;get&quot;</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=&quot;post&quot;</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&lt;poll_id&gt;\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': &quot;Vous n'avez pas sélectionné de choix.&quot;,
        })
    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">
&lt;h1&gt;{{ poll.question }}&lt;/h1&gt;

&lt;ul&gt;
{% for choice in poll.choice_set.all %}
    &lt;li&gt;{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}&lt;/li&gt;
{% endfor %}
&lt;/ul&gt;
</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&lt;poll_id&gt;\d+)/$', 'detail'),
    (r'^(?P&lt;poll_id&gt;\d+)/results/$', 'results'),
    (r'^(?P&lt;poll_id&gt;\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&lt;object_id&gt;\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
    (r'^(?P&lt;object_id&gt;\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html')),
    (r'^(?P&lt;poll_id&gt;\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">&quot;object_id&quot;</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">&lt;nom_appli&gt;/&lt;nom_module&gt;_detail.html</span></tt>. Dans notre cas, ce sera le template
appelé <tt class="docutils literal"><span class="pre">&quot;polls/poll_detail.html&quot;</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">&lt;nom_appli&gt;/&lt;nom_module&gt;_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>

