mirror of
https://github.com/django/django.git
synced 2025-12-15 21:45:20 +00:00
Fixed #18715 - Refactored tutorial 3. Thank-you Daniel Greenfeld!
This commit is contained in:
parent
08286ca5d9
commit
07abb7a6b7
3 changed files with 326 additions and 388 deletions
|
|
@ -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>`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue