4.1 Create a simple form

master
Jason Zhu 2020-10-20 19:24:12 +11:00
parent 624f61a563
commit e0267685cc
4 changed files with 128 additions and 10 deletions

View File

@ -247,3 +247,82 @@ Consider `index.html` will be reused numerous times, 去耦合is necessary.
1. Add `app_name = 'polls'` for namespace in `poll.urls.py`
2. Change `detail` in `index.html` to `polls:detail`
## Part 4. Form
### 4.1 编写一个简单的表单
Edit `polls/templates/polls/detail.html` so it contain **`HTML<form>`** elements:
```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>
```
Explain:
* 当有人选择一个单选按钮并提交表单提交时,它将发送一个 POST 数据 choice=# ,其中# 为选择的 Choice 的 ID。这是 HTML 表单的基本概念。
* 我们设置表单的 action 为 {% url 'polls:vote' question.id %} ,并设置 method="post" 。使用 method="post"``(与其相对的是 ``method="get"`)是非常重要的,因为这个提交表单的行为会改变服务器端的数据。 **无论何时,当你需要创建一个改变服务器端数据的表单时,请使用 ``method="post"** 。这不是 Django 的特定技巧;这是优秀的网站开发技巧。
* 所有针对内部 URL 的 POST 表单都应该使用 {% csrf_token %} 模板标签。
修改 `view.py`, 将Choice 逻辑灌输入其中。
```python
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
# request.POST 是一个类字典对象 (dictionary-like object),让你可以通过关键字的名字获取提交的数据。
# request.POST['choice'] 以字符串形式返回选择的 Choice 的 ID
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# 如果在 request.POST['choice'] 数据中没有提供 choice POST 将引发一个 KeyError 。上面的代码检查 KeyError ,如果没有给出 choice 将重新显示 Question 表单和一个错误信息。
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# 在增加 Choice 的得票数之后,代码返回一个 HttpResponseRedirect 而不是常用的 HttpResponse 、 HttpResponseRedirect 只接收一个参数:用户将要被重定向的 URL
# 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=(question.id,)))
# 我们在 HttpResponseRedirect 的构造函数中使用 reverse() 函数。这个函数避免了我们在视图函数中硬编码 URL。它需要我们给出我们想要跳转的视图的名字和该视图所对应的 URL 模式中需要给该视图提供的参数。在本例中,使用在 教程第 3 部分 中设定的 URLconf reverse() 调用将返回一个这样的字符串:'/polls/3/results/' 其中 3 是 question.id 的值。重定向的 URL 将调用 'results' 视图来显示最终的页面。
```
当有人对 Question 进行投票后, vote() 视图将请求重定向到 Question 的结果界面 (result view), edit it:
```python
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
```
同时,我们需要创建一个`polls/results.html` template
```python
<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>
```

View File

@ -1,6 +1,12 @@
<h1>{{ question.question_text }}</h1>
<ul>
{% 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 %}
<li>{{ choice.choice_text }}</li>
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
</ul>
<input type="submit" value="Vote">
</form>

View File

@ -0,0 +1,9 @@
<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>

View File

@ -1,8 +1,9 @@
from django.http import request, HttpResponse
from django.http import request, HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.template import loader
from django.urls import reverse
from .models import Question
from .models import Question, Choice
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
@ -14,8 +15,31 @@ def detail(request, question_id):
return render(request, 'polls/detail.html', {'question': question})
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, 'polls/results.html', {'question': question})
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
question = get_object_or_404(Question, pk=question_id)
try:
# request.POST 是一个类字典对象 (dictionary-like object),让你可以通过关键字的名字获取提交的数据。
# request.POST['choice'] 以字符串形式返回选择的 Choice 的 ID
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# 如果在 request.POST['choice'] 数据中没有提供 choice POST 将引发一个 KeyError 。上面的代码检查 KeyError ,如果没有给出 choice 将重新显示 Question 表单和一个错误信息。
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# 在增加 Choice 的得票数之后,代码返回一个 HttpResponseRedirect 而不是常用的 HttpResponse 、 HttpResponseRedirect 只接收一个参数:用户将要被重定向的 URL
# 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=(question.id,)))
# 我们在 HttpResponseRedirect 的构造函数中使用 reverse() 函数。这个函数避免了我们在视图函数中硬编码 URL。它需要我们给出我们想要跳转的视图的名字和该视图所对应的 URL 模式中需要给该视图提供的参数。在本例中,使用在 教程第 3 部分 中设定的 URLconf reverse() 调用将返回一个这样的字符串:'/polls/3/results/' 其中 3 是 question.id 的值。重定向的 URL 将调用 'results' 视图来显示最终的页面。