Django -- Polls - Part 4
2018-03-20 本文已影响0人
liaozb1996
Form
- 如果是从服务器获取信息,使用
GET
;如果是要从服务器修改信息,使用POST
;发生敏感信息,使用POST
- 上传文件,需要在
form
添加属性enctype="multipart/form-data"
HTML Form
# polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p style="color: red;"><strong>{{ error_message }}<strong></p>{% endif %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %} <!-- 防御跨站请求伪造,每个POST action指向同一个网站时必须包含 -->
{% for choice in question.choice_set.all %}
<input type="radio" id="choice{{ forloop.counter }}" name="choice" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{choice.choice_text}}</label>
{% endfor %}
<br />
<br />
<input type="submit" />
</form>
处理投票的视图
# polls/views.py
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = get_object_or_404(Choice, pk=request.POST['choice']) # 类似字典,POST的值都是字符串
except (KeyError, Choice.DoesNotExist):
# KeyError: 当 POST 中不存在 choice
error_message = 'Please select a choice!'
context = {'question': question, 'error_message': error_message}
return render(request, 'polls/detail.html', context)
else:
selected_choice.vote += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:result', args=(question_id, )))
# 每次成功处理 POST后都要进行重定向,避免用户点击 "后退" 时重复提交
def result(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/result.html', {'question': question})
投票加一的处理逻辑有一点问题:当两个用户同时投票,取得值 42,同时加一,保存到数据库的值会时43(正确应是 44)
【解决方法:Avoiding race conditions using F()】
# polls/templates/polls/result.html
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.vote }}</li>
{% endfor %}
</ul>
投票结果的模板
# polls/templates/polls/result.html
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.vote }} vote{{ choice.vote|pluralize }}</li>
{% endfor %}
</ul>
通用视图
开发Web通常会:根据URL从数据库获取数据,然后再渲染模板。
Django 提供了通用视图 generic 来简化步骤
修改 URL Pattern
# polls/urls.py
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/result/', views.ResultView.as_view(), name='result'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
使用通用视图
from django.views import generic
from .models import Question, Choice
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Question.objects.order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultView(generic.DetailView):
model = Question
template_name = 'polls/result.html'
分类
-
ListView
: display a list of objects -
DetailView
: display a detail page for a particular type of object.
- 每个通用视图都需要一个
model
变量 - DetailView 需要从URL中获得
pk
(primary key)
默认模板
- ListView:
<app name>/<model name>_list.html
- DetailVew:
<app name>/<model name>_detail.html
可以通过变量template_name
修改
默认 context
默认传递给模板的环境变量名
- ListView:
<model name>_list
(小写的model name) - DetailView:
<model name>
(小写)
可以通过变量context_object_name attribute
修改