From e0267685ccded4e30671112dbfe469b8ad6fdd3c Mon Sep 17 00:00:00 2001 From: JasonHomeWorkstationUbuntu Date: Tue, 20 Oct 2020 19:24:12 +1100 Subject: [PATCH] 4.1 Create a simple form --- first_django_app.md | 81 +++++++++++++++++++++++++- src/polls/templates/polls/detail.html | 12 +++- src/polls/templates/polls/results.html | 9 +++ src/polls/views.py | 36 ++++++++++-- 4 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 src/polls/templates/polls/results.html diff --git a/first_django_app.md b/first_django_app.md index 9f4df74..dd25a12 100644 --- a/first_django_app.md +++ b/first_django_app.md @@ -246,4 +246,83 @@ Consider `index.html` will be reused numerous times, 去耦合is necessary. 在根 URLconf 中添加命名空间 (namespace) 1. Add `app_name = 'polls'` for namespace in `poll.urls.py` -2. Change `detail` in `index.html` to `polls:detail` \ No newline at end of file +2. Change `detail` in `index.html` to `polls:detail` + +## Part 4. Form + +### 4.1 编写一个简单的表单 + +Edit `polls/templates/polls/detail.html` so it contain **`HTML
`** elements: + +```html +

{{ question.question_text }}

+ +{% if error_message %}

{{ error_message }}

{% endif %} + + +{% csrf_token %} +{% for choice in question.choice_set.all %} + +
+{% endfor %} + +
+``` + +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 +

{{ question.question_text }}

+ + + +Vote again? +``` \ No newline at end of file diff --git a/src/polls/templates/polls/detail.html b/src/polls/templates/polls/detail.html index 4e1bd67..a4e0635 100644 --- a/src/polls/templates/polls/detail.html +++ b/src/polls/templates/polls/detail.html @@ -1,6 +1,12 @@

{{ question.question_text }}

- \ No newline at end of file + + \ No newline at end of file diff --git a/src/polls/templates/polls/results.html b/src/polls/templates/polls/results.html new file mode 100644 index 0000000..33b67b2 --- /dev/null +++ b/src/polls/templates/polls/results.html @@ -0,0 +1,9 @@ +

{{ question.question_text }}

+ + + +Vote again? \ No newline at end of file diff --git a/src/polls/views.py b/src/polls/views.py index 53410df..ecda988 100644 --- a/src/polls/views.py +++ b/src/polls/views.py @@ -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) \ No newline at end of file + 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' 视图来显示最终的页面。