经过前面的努力,到这里我们已经基本完成了,从服务器到浏览器的数据下发,还没有解决从浏览器到服务器的数据上传,这一节将创建一个Form获取从浏览器提交的数据
1.新建Form
接着前面建的项目,网上调查,每一个Question有多个Choice,当用户针对特定问题,可以提交选择,数据库记录每个Choice的vote数,所以新建:
polls/templates/detail.html
编写代码如下:
<h1>{{ question.question_text }}</h1> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} {% for choice in question.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>
{% csrf_token %} django为了防止跨站点伪造请求,POST Form 应当在请求中使用{% csrf_token%}标签
{% url 'polls:vote' question.id %}的写法,是为了防止页面硬编码[查看上一篇内容],引入的url的另一种写法, polls:vote 的写法是给polls/urls.py 加了url命名空间:
1 from django.urls import path 2 from . import views 3 app_name="polls" 4 urlpatterns=[ 5 path('',views.index,name='index'), 6 path('<int:question_id>/detail',views.detail,name="detail"), #定义detail view 7 path('<int:question_id>/results',views.results,name='results'),#定义格式:/参数[参数类型为int]/results 8 #这里将原来的vote/<int:question_id>改成<Vote/<int:question_id> 9 path('vote/<int:question_id>',views.vote,name='vote') #定义格式:vote/参数[参数类型为int] 10 ]
上面建的表单要提交一个question_id 和 Choice_Id 到 /vote view,为了表单提交数据的安全性,这里指定使用POST传输,这点view没有特别要求,改造view/vote 对指定的question_id的choice_id的votes+1
2.接收Form数据并处理业务
修改polls/views.py/vote如下,(为了方便对比这里贴出来整个Views.py):
1 from django.shortcuts import get_object_or_404, render 2 from django.http import HttpResponseRedirect, HttpResponse 3 from django.urls import reverse 4 5 from .models import Choice, Question 6 # ... 7 def vote(request, question_id): 8 question = get_object_or_404(Question, pk=question_id) 9 try: 10 selected_choice = question.choice_set.get(pk=request.POST['choice']) 11 except (KeyError, Choice.DoesNotExist): 12 # Redisplay the question voting form. 13 return render(request, 'polls/detail.html', { 14 'question': question, 15 'error_message': "You didn't select a choice.", 16 }) 17 else: 18 selected_choice.votes += 1 19 selected_choice.save() 20 # Always return an HttpResponseRedirect after successfully dealing 21 # with POST data. This prevents data from being posted twice if a 22 # user hits the Back button. 23 return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) 24 25 def index(request): 26 latest_question_list = Question.objects.order_by('-pub_date')[:5] 27 28 context = { 29 'latest_question_list': latest_question_list, 30 } 31 return render(request,'polls/index.html',context) 32 # ... 33 def detail(request, question_id): 34 question = get_object_or_404(Question, pk=question_id) 35 return render(request, 'polls/detail.html', {'question': question}) 36 37 def results(request, question_id): 38 question = get_object_or_404(Question, pk=question_id) 39 return render(request, 'polls/results.html', {'question': question})
编辑polls/templates/polls/index.html如下:
{% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %}
以上完成了投票功能,最后需要对投票的结果进行展示,新建网页:polls/templates/polls/results.html
<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> {% endfor %} </ul> <a href="{% url 'polls:detail' question.id %}">Vote again?</a>
启动站点,浏览http://localhost:8000/polls/
投票完成之后,查询数据:
3.view改良
首先每张表都有主键,对polls/urls.py的改良就是将<int:question_id>替换成<int:pk>
from django.urls import path from . import views app_name = 'polls' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), path('<int:pk>/', views.DetailView.as_view(), name='detail'), path('<int:pk>/results/', views.ResultsView.as_view(), name='results'), path('<int:question_id>/vote/', views.vote, name='vote'), ]
polls/views.py中原来定义的index,detail,results 换成 Django generic的view
1 from django.shortcuts import get_object_or_404, render 2 from django.http import HttpResponseRedirect 3 from django.urls import reverse 4 from django.views import generic 5 from django.utils import timezone 6 7 from .models import Choice, Question 8 9 10 class IndexView(generic.ListView): 11 template_name = 'polls/index.html' 12 context_object_name = 'latest_question_list' 13 14 def get_queryset(self): 15 """Return the last five published questions.""" 16 return Question.objects.order_by('-pub_date')[:5] 17 18 19 class DetailView(generic.DetailView): 20 model = Question 21 template_name = 'polls/detail.html' 22 23 24 class ResultsView(generic.DetailView): 25 model = Question 26 template_name = 'polls/results.html' 27 28 def vote(request, question_id): 29 question = get_object_or_404(Question, pk=question_id) 30 try: 31 selected_choice = question.choice_set.get(pk=request.POST['choice']) 32 except (KeyError, Choice.DoesNotExist): 33 # Redisplay the question voting form. 34 return render(request, 'polls/detail.html', { 35 'question': question, 36 'error_message': "You didn't select a choice.", 37 }) 38 else: 39 selected_choice.votes += 1 40 selected_choice.save() 41 # Always return an HttpResponseRedirect after successfully dealing 42 # with POST data. This prevents data from being posted twice if a 43 # user hits the Back button. 44 return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
view vote牵涉到表单提交,数据库的操作,所以没有使用通用view改造