thirdparty/google_appengine/lib/django/docs/tutorial04.txt
changeset 109 620f9b141567
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thirdparty/google_appengine/lib/django/docs/tutorial04.txt	Tue Aug 26 21:49:54 2008 +0000
@@ -0,0 +1,259 @@
+=====================================
+Writing your first Django app, part 4
+=====================================
+
+This tutorial begins where `Tutorial 3`_ left off. We're continuing the Web-poll
+application and will focus on simple form processing and cutting down our code.
+
+Write a simple form
+===================
+
+Let's update our poll detail template from the last tutorial, so that the
+template contains an HTML ``<form>`` element::
+
+    <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>
+
+A quick rundown:
+
+    * The above template displays a radio button for each poll choice. The
+      ``value`` of each radio button is the associated poll choice's ID. The
+      ``name`` of each radio button is ``"choice"``. That means, when somebody
+      selects one of the radio buttons and submits the form, it'll send the
+      POST data ``choice=3``. This is HTML Forms 101.
+
+    * We set the form's ``action`` to ``/polls/{{ poll.id }}/vote/``, and we
+      set ``method="post"``. Using ``method="post"`` (as opposed to
+      ``method="get"``) is very important, because the act of submitting this
+      form will alter data server-side. Whenever you create a form that alters
+      data server-side, use ``method="post"``. This tip isn't specific to
+      Django; it's just good Web development practice.
+
+Now, let's create a Django view that handles the submitted data and does
+something with it. Remember, in `Tutorial 3`_, we created a URLconf for the
+polls application that includes this line::
+
+    (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
+
+So let's create a ``vote()`` function in ``mysite/polls/views.py``::
+
+    from django.shortcuts import get_object_or_404, render_to_response
+    from django.http import HttpResponseRedirect
+    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):
+            # Redisplay the poll voting form.
+            return render_to_response('polls/detail.html', {
+                'poll': p,
+                'error_message': "You didn't select a choice.",
+            })
+        else:
+            selected_choice.votes += 1
+            selected_choice.save()
+            # Always return an HttpResponseRedirect after successfully dealing
+            # with POST data. This prevents data from being posted twice if a
+            # user hits the Back button.
+            return HttpResponseRedirect('/polls/%s/results/' % p.id)
+
+This code includes a few things we haven't covered yet in this tutorial:
+
+    * ``request.POST`` is a dictionary-like object that lets you access
+      submitted data by key name. In this case, ``request.POST['choice']``
+      returns the ID of the selected choice, as a string. ``request.POST``
+      values are always strings.
+
+      Note that Django also provides ``request.GET`` for accessing GET data
+      in the same way -- but we're explicitly using ``request.POST`` in our
+      code, to ensure that data is only altered via a POST call.
+
+    * ``request.POST['choice']`` will raise ``KeyError`` if ``choice`` wasn't
+      provided in POST data. The above code checks for ``KeyError`` and
+      redisplays the poll form with an error message if ``choice`` isn't given.
+
+    * After incrementing the choice count, the code returns an
+      ``HttpResponseRedirect`` rather than a normal ``HttpResponse``.
+      ``HttpResponseRedirect`` takes a single argument: the URL to which the
+      user will be redirected. You should leave off the "http://" and domain
+      name if you can. That helps your app become portable across domains.
+
+      As the Python comment above points out, you should always return an
+      ``HttpResponseRedirect`` after successfully dealing with POST data. This
+      tip isn't specific to Django; it's just good Web development practice.
+
+As mentioned in Tutorial 3, ``request`` is a ``HTTPRequest`` object. For more
+on ``HTTPRequest`` objects, see the `request and response documentation`_.
+
+After somebody votes in a poll, the ``vote()`` view redirects to the results
+page for the poll. Let's write that view::
+
+    def results(request, poll_id):
+        p = get_object_or_404(Poll, pk=poll_id)
+        return render_to_response('polls/results.html', {'poll': p})
+
+This is almost exactly the same as the ``detail()`` view from `Tutorial 3`_.
+The only difference is the template name. We'll fix this redundancy later.
+
+Now, create a ``results.html`` template::
+
+    <h1>{{ poll.question }}</h1>
+
+    <ul>
+    {% for choice in poll.choice_set.all %}
+        <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
+    {% endfor %}
+    </ul>
+
+Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a
+results page that gets updated each time you vote. If you submit the form
+without having chosen a choice, you should see the error message.
+
+.. _request and response documentation: ../request_response/
+
+Use generic views: Less code is better
+======================================
+
+The ``detail()`` (from `Tutorial 3`_) and ``results()`` views are stupidly
+simple -- and, as mentioned above, redundant. The ``index()`` view (also from
+Tutorial 3), which displays a list of polls, is similar.
+
+These views represent a common case of basic Web development: getting data from
+the database according to a parameter passed in the URL, loading a template and
+returning the rendered template. Because this is so common, Django provides a
+shortcut, called the "generic views" system.
+
+Generic views abstract common patterns to the point where you don't even need
+to write Python code to write an app.
+
+Let's convert our poll app to use the generic views system, so we can delete a
+bunch of our own code. We'll just have to take a few steps to make the
+conversion.
+
+.. admonition:: Why the code-shuffle?
+
+    Generally, when writing a Django app, you'll evaluate whether generic views
+    are a good fit for your problem, and you'll use them from the beginning,
+    rather than refactoring your code halfway through. But this tutorial
+    intentionally has focused on writing the views "the hard way" until now, to
+    focus on core concepts.
+
+    You should know basic math before you start using a calculator.
+
+First, open the polls/urls.py URLconf. It looks like this, according to the
+tutorial so far::
+
+    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 it like so::
+
+    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),
+        (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/$', 'mysite.polls.views.vote'),
+    )
+
+We're using two generic views here: ``object_list`` and ``object_detail``.
+Respectively, those two views abstract the concepts of "display a list of
+objects" and "display a detail page for a particular type of object."
+
+    * Each generic view needs to know what data it will be acting upon. This
+      data is provided in a dictionary. The ``queryset`` key in this dictionary
+      points to the list of objects to be manipulated by the generic view.
+
+    * The ``object_detail`` generic view expects the ID value captured
+      from the URL to be called ``"object_id"``, so we've changed ``poll_id`` to
+      ``object_id`` for the generic views.
+
+By default, the ``object_detail`` generic view uses a template called
+``<app name>/<model name>_detail.html``. In our case, it'll use the template
+``"polls/poll_detail.html"``. Thus, rename your ``polls/detail.html`` template to
+``polls/poll_detail.html``, and change the ``render_to_response()`` line in
+``vote()``.
+
+Similarly, the ``object_list`` generic view uses a template called
+``<app name>/<model name>_list.html``. Thus, rename ``polls/index.html`` to
+``polls/poll_list.html``.
+
+Because we have more than one entry in the URLconf that uses ``object_detail``
+for the polls app, we manually specify a template name for the results view:
+``template_name='polls/results.html'``. Otherwise, both views would use the same
+template. Note that we use ``dict()`` to return an altered dictionary in place.
+
+.. note:: ``all()`` is lazy
+
+    It might look a little frightening to see ``Poll.objects.all()`` being used
+    in a detail view which only needs one ``Poll`` object, but don't worry;
+    ``Poll.objects.all()`` is actually a special object called a ``QuerySet``,
+    which is "lazy" and doesn't hit your database until it absolutely has to. By
+    the time the database query happens, the ``object_detail`` generic view will
+    have narrowed its scope down to a single object, so the eventual query will
+    only select one row from the database. 
+    
+    If you'd like to know more about how that works, The Django database API
+    documentation `explains the lazy nature of QuerySet objects`_.
+
+.. _explains the lazy nature of QuerySet objects: ../db_api/#querysets-are-lazy
+
+In previous parts of the tutorial, the templates have been provided with a context
+that contains the ``poll`` and ``latest_poll_list`` context variables. However,
+the generic views provide the variables ``object`` and ``object_list`` as context.
+Therefore, you need to change your templates to match the new context variables.
+Go through your templates, and modify any reference to ``latest_poll_list`` to
+``object_list``, and change any reference to ``poll`` to ``object``.
+
+You can now delete the ``index()``, ``detail()`` and ``results()`` views
+from ``polls/views.py``. We don't need them anymore -- they have been replaced
+by generic views.
+
+The ``vote()`` view is still required. However, it must be modified to match
+the new templates and context variables. Change the template call from
+``polls/detail.html`` to ``polls/poll_detail.html``, and pass ``object`` in the
+context instead of ``poll``.
+
+Run the server, and use your new polling app based on generic views.
+
+For full details on generic views, see the `generic views documentation`_.
+
+.. _generic views documentation: ../generic_views/
+
+Coming soon
+===========
+
+The tutorial ends here for the time being. But check back soon for the next
+installments:
+
+    * Advanced form processing
+    * Using the RSS framework
+    * Using the cache framework
+    * Using the comments framework
+    * Advanced admin features: Permissions
+    * Advanced admin features: Custom JavaScript
+
+.. _Tutorial 3: ../tutorial3/