目录
编写第一个django应用:做一个投票功能
效果图:
基础功能
逻辑
首先将数据取出,创建表单,利用for循环创建一个选项 包括:radio组件和lable组件
静态模板:
<form action="{% url 'vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
<legend><h1>{{ question.question_text }}</h1></legend>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
{% 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 %}
</fieldset>
<input type="submit" value="Vote">
</form>
{% csrf_token %}:表单安全协议的验证--->防御系统x
我们将表单的 action
设置为 {% url 'polls:vote' question.id %}
,并设置 method="post"
。使用 method="post"
(而不是 method="get"
)是非常重要的,因为提交这个表单的行为将改变服务器端的数据。当你创建一个改变服务器端数据的表单时,使用 method="post"
。这不是 Django 的特定技巧;这是优秀的网站开发技巧。
forloop.counter指示 [
for`]标签已经循环多少次。
视图
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from .models import Question, Choice
def index(request):
latest_question_list = Question.objects.order_by('pub_data')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'index.html', context)
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'detail.html', {'question': question})
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
question=get_object_or_404(Question,pk=question_id)
try:
select_choice=question.choice_set.get(pk=request.POST['choice'])
except(KeyError,Choice.DoesNotExist):
return render(request, 'detail.html',{
'question':question,
'error_message':'you did not select a choice',
})
else:
select_choice.votes+=1
select_choice.save()
return HttpResponseRedirect(reverse('results',args=(question.id,)))
路由
urlpatterns = [
path('study01/',views.index),
path('study01/<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('study01/<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('study01/<int:question_id>/vote/', views.vote, name='vote'),
]
-
request.POST 是一个类字典对象,让你可以通过关键字的名字获取提交的数据。 这个例子中,
request.POST['choice']
以字符串形式返回选择的 Choice 的 ID。 request.POST 的值永远是字符串。注意,Django 还以同样的方式提供 request.GET 用于访问 GET 数据 —— 但我们在代码中显式地使用 request.POST ,以保证数据只能通过 POST 调用改动。
-
如果在
request.POST['choice']
数据中没有提供choice
, POST 将引发一个 KeyError 。上面的代码检查 KeyError ,如果没有给出choice
将重新显示 Question 表单和一个错误信息。 -
在增加 Choice 的得票数之后,代码返回一个 HttpResponseRedirect 而不是常用的 HttpResponse 、 HttpResponseRedirect 只接收一个参数:用户将要被重定向的 URL(请继续看下去,我们将会解释如何构造这个例子中的 URL)。
正如上面的 Python 注释指出的,在成功处理 POST 数据后,你应该总是返回一个 HttpResponseRedirect。这不是 Django 的特殊要求,这是那些优秀网站在开发实践中形成的共识。
-
在这个例子中,我们在 HttpResponseRedirect 的构造函数中使用
reverse()
函数。这个函数避免了我们在视图函数中硬编码 URL。它需要我们给出我们想要跳转的视图的名字和该视图所对应的 URL 模式中需要给该视图提供的参数。 在本例中,使用在 教程第 3 部分 中设定的 URLconf,reverse()
调用将返回一个这样的字符串:
继续完善:
投完票以后显示投票的结果
则接的路由:results
静态页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<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 'detail' question.id %}">Vote again?</a>
</body>
</html>
视图:增加
def results(request, question_id):
# response = "You're looking at the results of question %s."
# return HttpResponse(response % question_id)
question=get_object_or_404(Question,pk=question_id)
return render(request, 'results.html',{'question':question})
到这一步我们我发现所有的代码已经写完了,但是发现视图代码太过于繁琐性,接下来我们需要改良代码
代码改良,成为固定通用模板
首先重构url:
urlpatterns = [
path('study01/',views.IndexView.as_view(),name='index'),
path('study01/<int:pk>/', views.DetailView.as_view(), name='detail'),
# ex: /polls/5/results/
path('study01/<int:pk>/results/', views.ResultsView.as_view(), name='results'),
# ex: /polls/5/vote/
path('study01/<int:question_id>/vote/', views.vote, name='vote'),
]
其次重写视图:
from django.views import generic
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from .models import Question, Choice
class IndexView(generic.ListView):
template_name = 'index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('pub_data')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'results.html'
def vote(request, question_id):
question=get_object_or_404(Question,pk=question_id)
try:
select_choice=question.choice_set.get(pk=request.POST['choice'])
except(KeyError,Choice.DoesNotExist):
return render(request, 'detail.html',{
'question':question,
'error_message':'you did not select a choice',
})
else:
select_choice.votes+=1
select_choice.save()
return HttpResponseRedirect(reverse('results',args=(question.id,)))
要求掌握
templates模板
重定向