Fixed #18715 - Refactored tutorial 3. Thank-you Daniel Greenfeld!

This commit is contained in:
Tim Graham 2012-10-13 14:37:39 -04:00
parent 08286ca5d9
commit 07abb7a6b7
3 changed files with 326 additions and 388 deletions

View file

@ -18,7 +18,7 @@ tutorial, so that the template contains an HTML ``<form>`` element:
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls.views.vote' poll.id %}" method="post">
<form action="{% url 'polls:vote' poll.id %}" method="post">
{% csrf_token %}
{% for choice in poll.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
@ -35,7 +35,7 @@ A quick rundown:
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 ``{% url 'polls.views.vote' poll.id %}``, and we
* We set the form's ``action`` to ``{% url 'polls:vote' poll.id %}``, 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
@ -52,34 +52,18 @@ A quick rundown:
forms that are targeted at internal URLs should use the
:ttag:`{% csrf_token %}<csrf_token>` template tag.
The :ttag:`{% csrf_token %}<csrf_token>` tag requires information from the
request object, which is not normally accessible from within the template
context. To fix this, a small adjustment needs to be made to the ``detail``
view, so that it looks like the following::
from django.template import RequestContext
# ...
def detail(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
return render_to_response('polls/detail.html', {'poll': p},
context_instance=RequestContext(request))
The details of how this works are explained in the documentation for
:ref:`RequestContext <subclassing-context-requestcontext>`.
Now, let's create a Django view that handles the submitted data and does
something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, we
created a URLconf for the polls application that includes this line::
(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
We also created a dummy implementation of the ``vote()`` function. Let's
create a real version. Add the following to ``polls/views.py``::
from django.shortcuts import get_object_or_404, render_to_response
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from django.template import RequestContext
from polls.models import Choice, Poll
# ...
def vote(request, poll_id):
@ -88,17 +72,17 @@ create a real version. Add the following to ``polls/views.py``::
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', {
return render(request, 'polls/detail.html', {
'poll': p,
'error_message': "You didn't select a choice.",
}, context_instance=RequestContext(request))
})
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(reverse('polls.views.results', args=(p.id,)))
return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
This code includes a few things we haven't covered yet in this tutorial:
@ -142,8 +126,7 @@ This code includes a few things we haven't covered yet in this tutorial:
'/polls/3/results/'
... where the ``3`` is the value of ``p.id``. This redirected URL will
then call the ``'results'`` view to display the final page. Note that you
need to use the full name of the view here (including the prefix).
then call the ``'results'`` view to display the final page.
As mentioned in Tutorial 3, ``request`` is a :class:`~django.http.HttpRequest`
object. For more on :class:`~django.http.HttpRequest` objects, see the
@ -153,14 +136,14 @@ 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})
poll = get_object_or_404(Poll, pk=poll_id)
return render(request, 'polls/results.html', {'poll': poll})
This is almost exactly the same as the ``detail()`` view from :doc:`Tutorial 3
</intro/tutorial03>`. The only difference is the template name. We'll fix this
redundancy later.
Now, create a ``results.html`` template:
Now, create a ``polls/results.html`` template:
.. code-block:: html+django
@ -172,7 +155,7 @@ Now, create a ``results.html`` template:
{% endfor %}
</ul>
<a href="{% url 'polls.views.detail' poll.id %}">Vote again?</a>
<a href="{% url 'polls:detail' poll.id %}">Vote again?</a>
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
@ -215,19 +198,7 @@ Read on for details.
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 import patterns, url
urlpatterns = patterns('polls.views',
url(r'^$', 'index'),
url(r'^(?P<poll_id>\d+)/$', 'detail'),
url(r'^(?P<poll_id>\d+)/results/$', 'results'),
url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
)
Change it like so::
First, open the ``polls/urls.py`` URLconf and change it like so::
from django.conf.urls import patterns, url
from django.views.generic import DetailView, ListView
@ -239,18 +210,18 @@ Change it like so::
queryset=Poll.objects.order_by('-pub_date')[:5],
context_object_name='latest_poll_list',
template_name='polls/index.html'),
name='poll_index'),
name='index'),
url(r'^(?P<pk>\d+)/$',
DetailView.as_view(
model=Poll,
template_name='polls/detail.html'),
name='poll_detail'),
name='detail'),
url(r'^(?P<pk>\d+)/results/$',
DetailView.as_view(
model=Poll,
template_name='polls/results.html'),
name='poll_results'),
url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
name='results'),
url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'),
)
We're using two generic views here:
@ -267,15 +238,6 @@ two views abstract the concepts of "display a list of objects" and
``"pk"``, so we've changed ``poll_id`` to ``pk`` for the generic
views.
* We've added the ``name`` argument to the views (e.g. ``name='poll_results'``)
so that we have a way to refer to their URL later on (see the
documentation about :ref:`naming URL patterns
<naming-url-patterns>` for information). We're also using the
:func:`~django.conf.urls.url` function from
:mod:`django.conf.urls` here. It's a good habit to use
:func:`~django.conf.urls.url` when you are providing a
pattern name like this.
By default, the :class:`~django.views.generic.list.DetailView` generic
view uses a template called ``<app name>/<model name>_detail.html``.
In our case, it'll use the template ``"polls/poll_detail.html"``. The
@ -308,41 +270,13 @@ 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 last thing to do is fix the URL handling to account for the use of
generic views. In the vote view above, we used the
:func:`~django.core.urlresolvers.reverse` function to avoid
hard-coding our URLs. Now that we've switched to a generic view, we'll
need to change the :func:`~django.core.urlresolvers.reverse` call to
point back to our new generic view. We can't simply use the view
function anymore -- generic views can be (and are) used multiple times
-- but we can use the name we've given::
return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
The same rule apply for the :ttag:`url` template tag. For example in the
``results.html`` template:
.. code-block:: html+django
<a href="{% url 'poll_detail' poll.id %}">Vote again?</a>
Run the server, and use your new polling app based on generic views.
For full details on generic views, see the :doc:`generic views documentation
</topics/class-based-views/index>`.
Coming soon
===========
What's next?
============
The tutorial ends here for the time being. Future installments of the tutorial
will cover:
* Advanced form processing
* Using the RSS framework
* Using the cache framework
* Using the comments framework
* Advanced admin features: Permissions
* Advanced admin features: Custom JavaScript
In the meantime, you might want to check out some pointers on :doc:`where to go
from here </intro/whatsnext>`
The tutorial ends here for the time being. In the meantime, you might want to
check out some pointers on :doc:`where to go from here </intro/whatsnext>`.