django (七) 第一个django app 创建表单
首先,在polls/detail.html里添加<form>:
<h1>{{ poll.question }}</h1> {% if error_message %} <p><strong>{{ error_message }}</strong></p> {% endif %} <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 }}" /> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br /> {% endfor %} <input type="submit" value="Vote" /> </form>
下面一次解释一下这段代码的含义:
它的主要功能就是在POLL下选择一个choice,然后点击button提交表单。
- 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 thisform 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.
- forloop.counter indicates how many times the for tag has gone through its loop
- Since we’re creating a POST form (which can have the effect of modifying data), we need to worry about CrossSite Request Forgeries. Thankfully, you don’t have to worry too hard, because Django comes with a very easyto-use system for protecting against it. In short, all POST forms that are targeted at internal URLs should use the {% csrf_token %} template tag.
这一步完成了后,先别刷新页面。和之前一样,修改polls/view.py如下:
# -*- coding: utf-8 -*- from django.shortcuts import render, get_object_or_404 from django.http import HttpResponseRedirect, HttpResponse from django.core.urlresolvers import reverse from polls.models import Poll,Choice def index(request): latest_poll_list = Poll.objects.order_by('-pub_date')[:5] context = {'latest_poll_list':latest_poll_list,} return render(request, 'polls/index.html', context) def detail(request, poll_id): poll = get_object_or_404(Poll, pk=poll_id) return render(request,'polls/detail.html', {'poll': poll}) def results(request,poll_id): return HttpResponse("You’re looking at the results of poll %s." %poll_id) 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(request, '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(reverse('polls:results', args=(p.id,)))
- 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 arealways strings.Note that Django also provides request.GET for accessing GET data in the same way – but we’re explicitlyusing 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 abovecode 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 (see the following point for how we construct the URL in this case). As the Python comment above points out, you should always return an HttpResponseRedirect aftersuccessfully dealing with POST data. This tip isn’t specific to Django; it’s just goodWeb development practice.
- We are using the reverse() function in the HttpResponseRedirect constructor in this example. This
function helps avoid having to hardcode a URL in the view function. It is given the name of the view that we
want to pass control to and the variable portion of the URL pattern that points to that view. In this case, using
the URLconf we set up in Tutorial 3, this reverse() call will return a string like’/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.
接下来,修改results:
def results(request, poll_id): poll = get_object_or_404(Poll, pk=poll_id) return render(request, 'polls/results.html', {'poll': poll})
然后新建result.html如下:
<h1>{{ poll.question }}</h1> <ul> {% for choice in poll.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> {% endfor %} </ul> <a href="{% url 'polls:detail' poll.id %}">Vote again?</a>
整个项目已基本完成,但是稍显繁琐,下面使用generic view系统来改造他一下:
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. We will:
1. Convert the URLconf.
2. Delete some of the old, unneeded views.
3. Introduce new views based on Django’s generic views.
Read on for details.
首先,打开polls/urls.py,修改如下:
from django.conf.urls import patterns, url from polls import views urlpatterns = patterns('', url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'), url(r'^(?P<pk>\d+)/results/$', views.ResultsView.as_view(), name='results'), url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'), )
接下来,修改polls/views.py如下:
from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse from django.views import generic from polls.models import Choice, Poll class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_poll_list' def get_queryset(self): """Return the last five published polls.""" return Poll.objects.order_by('-pub_date')[:5] class DetailView(generic.DetailView): model = Poll template_name = 'polls/detail.html' class ResultsView(generic.DetailView): model = Poll template_name = 'polls/results.html' 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(request, '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(reverse('polls:results', args=(p.id,)))