Bruno Renié avatar Bruno Renié committed 2cdd9d8

Traduction de intro/tutorial04.txt (rev 9732)

Comments (0)

Files changed (1)

docs/intro/tutorial04.txt

 .. _intro-tutorial04:
 
-.. warning:: Document en cours de traduction
+==================================================
+Écrire votre première application Django, partie 3
+==================================================
 
-    Nous sommes désolés, ce document n'est pas encore traduit.
-    Vous pouvez lire la version originale ici :
-    http://docs.djangoproject.com/en/dev/intro/tutorial04/
-    Une traduction d'une ancienne version est également disponible :
-    http://www.django-fr.org/documentation/tutorial04/
+Ce tutoriel commence là où le :ref:`Tutoriel 3 <intro-tutorial03>` s'achève.
+Nous continuons l'application de sondage Web et allons nous focaliser la
+gestion des formulaires et sur la réduction du code.
 
+Écrire un simple formulaire
+===========================
+
+Nous allons mettre à jour le template de la page de détail
+("polls/details.html") du dernier tutoriel, de manière que le template
+contienne une balise HTML ``<form>`` :
+
+.. code-block:: html+django
+
+    <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="Vote" />
+    </form>
+
+Un résumé rapide :
+
+    * Ce template affiche un bouton radio pour chaque choix. L'attribut
+      ``value`` de chaque bouton radio correspond à l'ID du vote choisi. Le
+      nom (``name``) de chaque bouton radio est ``"choice"``. Cela signifie
+      que lorsque quelqu'un sélectionne l'un des boutons radio et valide le
+      formulaire, les données POST ``choice=3`` seront envoyées. Ce sont les
+      formulaires HTML 101.
+
+    * Nous avons défini ``/polls/{{ poll.id }}/vote/`` comme attribut
+      ``action`` du formulaire, et nous précisons ``method=post``.
+      L'utilisation de ``method="post"`` (par opposition à ``method="get"``)
+      est très importante, puisque le fait de valider ce formualire va
+      entrainer des modifications de données sur le serveur. À chaque fois
+      qu'un formulaire modifie des données sur le serveur, vous devez utiliser
+      ``method="post"``. Cela ne concerne pas uniquement Django ; c'est une
+      bonne pratique à adopter en tant que développeur Web.
+
+    * ``forloop.counter`` indique combien de fois la balise :ttag:`for` a
+      exécuté sa boucle.
+
+Maintenant, nous allons créer une vue qui récupère les données envoyées pour
+nous permettre de faire des choses avec. Souvenez-vous, dans le :ref:`Tutoriel 3
+<intro-tutorial03>`, nous avons créé un URLconf pour l'application de sondage
+contenant cette ligne::
+
+    (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote')
+
+Écrivons donc une fonction ``vote()`` dans ``mysite/polls/views.py``::
+
+    from django.shortcuts import get_object_or_404, render_to_response
+    from django.http import HttpResponseRedirect
+    from django.core.urlresolvers import reverse
+    from mysite.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éafficher le formulaire de vote du sondage.
+            return render_to_response('polls/detail.html', {
+                'poll': p,
+                'error_message': "Veuillez sélectionner une réponse.",
+            })
+        else:
+            selected_choice.votes += 1
+            selected_choice.save()
+            # Toujours renvoyer une HttpResponseRedirect après avoir
+	    # correctement traité les données POST. Cela empêche de poster les
+	    # données deux fois si l'utilisateur appuie sur le bouton
+	    # "précedent".
+            return HttpResponseRedirect(reverse('mysite.polls.views.results', args=(p.id,)))
+
+Ce code contient quelques points encore non traités dans ce tutoriel :
+    * :attr:`request.POST <django.http.HttpRequest.POST>` est un objet
+      similaire à un dictionnaire qui vous permet d'accéder aux données
+      envoyées par leurs clés. Dans ce cas, ``request.POST['choice']`` renvoie
+      l'ID du choix sélectionné, sous forme d'une chaîne de caractères. Les
+      valeurs dans :attr:`request.POST <django.http.HttpRequest.POST>` sont
+      toujours des chaînes de caractères.
+
+      Notez que Django dispose aussi de :attr:`request.GET
+      <django.http.HttpRequest.GET>` pour accéder aux données GET de la même
+      manière -- mais nous utilisons explicitement :attr:`request.POST
+      <django.http.HttpRequest.POST>` dans notre code, pour s'assurer que les
+      données ne sont modifiées que par des requêtes POST.
+
+    * ``request.POST['choice']`` lèvera une exception  :exc:`KeyError` si
+      ``choice`` n'est pas spécifié dans les données POST. Le code ci-dessus
+      vérifie qu'une exception :exc:`KeyError` n'est pas levée et réaffiche le
+      formulaire de sondage avec un message d'erreur si ``choice`` n'est pas
+      rempli.
+
+    * Après l'incrémentation du numéro du choix, le code renvoie une
+      :class:`~django.http.HttpResponseRedirect` plutôt qu'une
+      :class:`~django.http.HttpResponse` normale.
+      :class:`~django.http.HttpResponseRedirect` prend un seul argument :
+      l'URL vers laquelle l'utilisateur va être redirigé (voir le point
+      suivant pour la manière de construire cette URL dans ce cas).
+
+      Comme le commentaire l'indique, vous devez systématiquement renvoyer une
+      :class:`~django.http.HttpResponseRedirect` après avoir correctement
+      traité les données POST. Ceci n'est pas valable uniquement avec Django,
+      c'est une bonne pratique du développement Web.
+
+    * Dans cet exemple, nous utilisons la fonction
+      :func:`~django.core.urlresolvers.reverse` dans le contructeur de
+      :class:`~django.http.HttpResponseRedirect`. Cette fonction nous évite de
+      coder en dur une URL dans une vue. On lui donne en paramètre la vue vers
+      laquelle nous voulons rediriger et la partie variable de l'URL qui
+      pointe vers cette vue. Dans ce cas, en utilisant l'URLconf défini dans
+      la partie 3 de ce tutoriel, l'appel de la fonction
+      :func:`~django.core.urlresolvers.reverse` va retourner une chaîne de
+      caractères::
+
+        '/polls/3/results/'
+
+      ... où ``3`` est la valeur de ``p.id``. Cette URL de redirection va être
+      ensuite appeler la vue ``'results'`` pour afficher la page finale. Notez
+      que vous devez utiliser le nom complet de la vue, préfixe compris.
+
+Comme expliqué dans la partie 3 de ce tutoriel, ``request`` est un objet
+:class:`~django.http.HttpRequest`. Pour plus d'informations sur les objets
+:class:`~django.http.HttpRequest`, voir la :ref:`documentation des requêtes et
+réponses <ref-request-response>`.
+
+Après l vote d'une personne dans un sondage, la vue ``vote()`` redirige vers
+la page de résultats du sondage. Écrivons cette vue ::
+
+    def results(request, poll_id):
+        p = get_object_or_404(Poll, pk=poll_id)
+        return render_to_response('polls/results.html', {'poll': p})
+
+C'est presque exactement la même que la vue ``detail()`` du :ref:`Tutoriel 3
+<intro-tutorial03>`. La seule différence est le nom du template. Nous
+éliminerons cette redondance plus tard.
+
+Écrivins maintenant le template ``results.html``:
+
+.. code-block:: html+django
+
+    <h1>{{ poll.question }}</h1>
+
+    <ul>
+    {% for choice in poll.choice_set.all %}
+        <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
+    {% endfor %}
+    </ul>
+
+Maintenant, rendez-vous à la page ``/polls/1/`` avec votre navigateur et votez
+au sondage proposé. Vous devriez voir une page de résultats qui sera mise à
+jour à chaque fois que vous voterez. Si vous validez le formulaire sans avoir
+coché votre choix, vous devriez voir le message d'erreur.
+
+Utilisation des vues génériques : moins de code, c'est mieux
+============================================================
+
+Les vues ``detail()`` (dans le :ref:`Tutoriel 3 <intro-tutorial03>`) et
+``results()`` sont triviales -- et comme mentionné précédemment, redondantes.
+La vue ``index()`` (aussi dans le Tutoriel 3), qui affiche une liste de
+sondages, est similaire.
+
+Ces vues représentent un cas classique du développement Web : récupérer les
+données depuis la base de données suivant un paramètre contenu dans l'URL,
+charger un template et renvoyer le template interprété. Ce cas est tellement
+classique que Django propose un raccourci, appelé le système de "vues
+génériques".
+
+Les vues génériques permettent l'abstraction de pratiques communes, à un tel
+point que vous n'avez pas à écrire du code python pour écrire une application.
+
+Nous allons convertir notre application de sondage pour qu'elle utilise le
+système de vues génériques, nous pourrons ainsi supprimer une partie de notre
+code. Nous acons juste quelques pas à faire pour faire cette conversion.
+
+.. admonition:: Pourquoi ce mélange ?
+
+    En général, lorsque vous écrirez une application Django, vous estimerez
+    que les vues génériques correspondent bien à vos besoins, et vous les
+    utiliserez à partir du début, plutôt que de réarranger votre code à
+    mi-chemin. Mais ce tutoriel s'est concentré intentionellement sur
+    l'écriture des vues "à la dure" jusque maintenant, pour mettre l'accent
+    sur les concepts de base.
+
+    Vous devez avoir des bases en maths avant de commencer à utiliser une
+    calculatrice.
+
+Tout d'abord, ouvrez l'URLconf ``polls/urls.py``. Il ressemble à ceci, si vous
+avez suivi le tutoriel jusqu'ici ::
+
+    from django.conf.urls.defaults import *
+
+    urlpatterns = patterns('mysite.polls.views',
+        (r'^$', 'index'),
+        (r'^(?P<poll_id>\d+)/$', 'detail'),
+        (r'^(?P<poll_id>\d+)/results/$', 'results'),
+        (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
+    )
+
+Change-le en ::
+
+    from django.conf.urls.defaults import *
+    from mysite.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),
+	url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'),
+	(r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
+    )
+
+Nous utilisons ici deux vues génériques :
+:func:`~django.views.generic.list_detail.object_list` et
+:func:`~django.views.generic.list_detail.object_detail`. Respectivement, ces
+deux vues permettent l'abstraction de "afficher une liste d'objets" et
+"afficher une page détaillée pour un type particulier d'objet".
+
+    * Chaque vue générique a besoin de connaître les données sur lesquelles
+      elles vont agir. Ces données sont fournies dans un dictionnaire. La clé
+      ``queryset`` de ce dictionnaire pointe vers la liste d'objets qui
+      doivent être manipulés par la vue générique.
+
+    * La vue générique :func:`~django.views.generic.list_detail.object_detail`
+      s'attend à ce que l'ID contenu dans l'URL s'appelle ``"object_id"``,
+      nous avons donc changé ``poll_id`` en ``object_id`` pour la vue
+      générique.
+
+    * Nous avons ajouté un nom -- ``poll_results`` -- à la vue des résultats
+      de manière à avoir une manière de faire référence à son URL plus tard
+      (voir la documentation à propos du :ref:`nommage des URL
+      <naming-url-patterns>` pour plus d'information). Nous utilisons aussi la
+      fonction :func:`~django.conf.urls.default.url` de
+      :mod:`django.conf.urls.defaults`. C'est une bonne habitude d'utiliser
+      :func:`~django.conf.urls.defaults.url` lorsque vous définissez un nom de
+      schéma comme celui-ci.
+
+Par défaut, la vue générique
+:func:`~django.views.generic.list_detail.object_detail` utilise un template
+appelé ``<app name>/<model name>_detail.html``. Dans notre cas, elle va
+utiliser le template ``"polls/poll_detail.html"``. Ainsi, renommez votre
+template ``polls/detail.html`` en ``polls/poll_detail.html``, et changez la
+ligne :func:`~django.shortcuts.render_to_response` dans ``vote()``.
+
+De la même façon, la vue générique
+:func:`~django.views.generic.list_detail.object_list` utilise un template
+appelé ``<app name>/<model name>_list.html``. Ainsi, renommez
+``polls/index.html`` en ``polls/poll_list.html``.
+
+Comme nous avons plus d'une entrée dans l'URLconf qui utilise
+:func:`~django.views.generic.list_detail.object_detail` pour l'application de
+sondages, nous précisons manuellement un nom de template pour la vue des
+résultats : ``template_name='polls/results.html'``. Sinon, les deux vues
+utiliseraient le même template. Notez que nous utilisons ``dict()`` pour
+renvoyer un dictionnaire altéré.
+
+.. note:: :meth:`django.db.models.QuerySet.all` est paresseux
+
+    Cela pourrait paraitre un peu effrayant de voir ``Poll.objects.all()``
+    utilisé dans une vue de détail qui ne nécessite qu'un seul objet ``Poll``,
+    mais ne vous inquiétez pas ; ``Poll.objects.all()`` est en fait un objet
+    spécial appelé :class:`~django.db.models.QuerySet`, qui est "paresseux" et
+    ne touche pas à la base de données tant qu'il n'en a pas absolument
+    besoin. Pendant le temps de la requête sur la base de données, la vue
+    génrique :func:`~django.views.generic.list_detail.object_detail` aura
+    réduit sa portée à un seul objet, et l'éventuelle requête ne va
+    sélectionner qu'un seul objet dans la base de données.
+
+    Si vous voulez en savoir plus sur le fonctionnement de tout ceci, la
+    documentation de l'API de base de données de Django :ref:`explique la
+    nature paresseuse des objets QuerySet <querysets-are-lazy>`.
+
+Dans les parties précedentes de ce tutoriel, les templates ont été fournies
+par un contexte qui contenait les variables de contexte ``poll`` et
+``latest_poll_list``. Cependant, les vues génériques fournissent les variables
+``object`` et ``object_list`` comme contexte. Par conséquent, vous devez
+changer vos templates, et modifier toutes les références à
+``latest_poll_list`` en ``object_list``, et changer les références à ``poll``
+en ``object``.
+
+Vous pouvez maintenant supprimer les vues ``index()``, ``detail()`` et
+``results()`` dans ``polls/views.py``. Nous n'en avons plus besoin -- elles
+ont été remplacées par les vues génériques.
+
+La vue ``vote()`` est encore requise. Cependant, elle doit être modifiée pour
+correspondre aux nouvelles variables de contexte. Dans l'appel à
+:func:`~django.shortcuts.render_to_response`, renommez la variable de contexte
+``poll`` en ``object``.
+
+la dernière chose à faire est corriger la gestion des URL pour prendre en
+compte la gestion des vues génériques. Dans la vue de vote précédente, nous
+avons utilisé la fonction :func:`~django.core.urlresolvers.reverse` pour
+éviter de coder en dur nos URLs. Depuis que nous sommes passé aux vues
+génériques, nous devons changer l'appel à
+:func:`~django.core.urlresolvers.reverse` pour pointer de nouveau sur notre
+nouvelle vue générique. Nous ne pouvons plus utiliser la fonction de vue --
+les vues génériques peuvent être (et sont) utilisées plusieurs fois -- mais
+nous pouvons utiliser le nom que nous avons donnée ::
+
+    return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
+
+Lancez le serveur, et utilisez votre nouvelle application de sondage utilisant
+les vues génériques.
+
+Pour plus de détails sur les vues génériques, voir la :ref:`documentation des
+vues génériques <topics-http-generic-views>`.
+
+Bientôt
+=======
+
+Le tutoriel s'arrête pour l'instant ici. Les prochaines mises à jour de ce
+tutoriel couvriront :
+
+    * Gestion avancée des formulaires
+    * Utilisation du framework RSS
+    * Utilisation du framework de cache
+    * Utilisation du framework de commentaires
+    * Fonctionnalités avancées de l'interface admin : Permissions
+    * Fonctionnalités avancées de l'interface admin : JavaScript personnalisé
+
+En attendant, vous devriez trouver quelques liens dans :ref:`où aller à partir
+d'ici <intro-whatsnext>`
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.