本文是在 python 的web框架 django 的入門教程 3 上開始的。根據 django 官方教程 學習而來,內容也與其教程4配對。
下一節內容是:python 的web框架 django 的入門教程 5 測試(待完成)
本章主要介紹表單和通用視圖。
寫個簡單的表單
修改模板:polls/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>
簡短總結下:
上面的模板爲每個問題選擇顯示一個單選按鈕。每個單選按鈕的值是相關問題選擇的ID。每個單選按鈕的名稱爲“選擇”。這就是說,當某人選擇一個單選按鈕並提交表單時,它將發送POST數據choice =#,其中#是所選選項的ID。這是HTML表單的基本概念。
將表單的action設置爲{%url'polls:vote'question.id%},設置method =“ post”。使用method =“ post”(而不是method =“ get”)非常重要,因爲提交此表單的行爲將改變服務器端的數據。創建更改數據服務器端的表單時,使用method =“ post”。
forloop.counter 表示 for循環 的次數,第1次循環爲1,然後是2,3
由於我們正在創建POST表單(可能具有修改數據的效果),因此我們需要擔心跨站點請求僞造。值得慶幸的是,不必太擔心,因爲Django隨附了一個非常易用的系統來防禦它。所有針對內部URL的POST表單都應使用{%csrf_token%}模板標記。
表單提交爲vote,下面修改vote。
修改polls/view.py 中的vote()函數
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from .models import Choice, Question
# ...
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# 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()
# 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,)))
該代碼包含了本教程中尚未涉及的一些內容:
request.POST是一個類似於字典的對象,可讓您通過鍵名訪問提交的數據。在這種情況下,request.POST ['choice']以字符串形式返回所選選項的ID。 request.POST值始終是字符串。
如果POST數據中未提供選擇,則request.POST ['choice']將引發KeyError。上面的代碼檢查KeyError並在未給出選擇的情況下重新顯示帶有錯誤消息的問題表格。
增加選擇計數後,代碼將返回HttpResponseRedirect而不是常規的HttpResponse。 HttpResponseRedirect接受一個參數:將用戶重定向到的URL(有關在這種情況下我們如何構造URL的信息,請參見以下幾點)。
正如上面的Python註釋所指出的那樣,在成功處理POST數據後,應該始終返回HttpResponseRedirect。本技巧並非專門針對Django;這只是Web開發的好習慣。
在此示例中,我們在HttpResponseRedirect構造函數中使用了reverse()函數。此功能有助於避免必須在視圖功能中對URL進行硬編碼。它提供了我們想要將控制權傳遞給的視圖的名稱,以及指向該視圖的URL模式的可變部分。在這種情況下,使用我們在教程3中設置的URLconf,此reverse()調用將返回類似
'/polls/3/results/'
其中3是question.id的值。 然後,此重定向的URL將調用“結果”視圖以顯示最終頁面。
下面我們要建立results view。
修改polls/view.py 中的results()函數:
from django.shortcuts import get_object_or_404, render
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
<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>
投票表單頁面detail:
投票結果顯示頁面results:
投票頁面裏什麼也不選,直接vote,出現報錯頁面:
通用視圖:減少代碼
detail()和results()視圖非常簡單,卻很冗餘。 顯示投票列表的index()視圖與此類似。
這些視圖代表了基本Web開發的一種常見情況:根據URL中傳遞的參數從數據庫獲取數據,加載模板並返回渲染的模板。 因爲這很常見,所以Django提供了一個捷徑,稱爲“通用視圖”。
通用視圖將常見的模式抽象爲甚至不需要編寫Python代碼即可編寫應用程序的地步。
讓我們將polls程序轉換爲使用通用視圖系統,刪除一堆自己的代碼。 我們只需要採取一些步驟即可進行轉換。 我們會:
轉換URLconf。
刪除一些舊的不需要的視圖。
根據Django的通用視圖引入新視圖。
修改URLconf
修改polls/urls.py
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'),
]
請注意,第二個和第三個模式的路徑字符串中匹配模式的名稱已從<question_id>更改爲<pk>。
修改view
修改polls/views.py
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from .models import Choice, Question
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
def vote(request, question_id):
... # same as above, no changes needed.
這裏使用兩個通用視圖:ListView和DetailView。這兩個視圖分別抽象了“顯示對象列表”和“顯示特定類型的對象的詳細信息頁面”的概念。
每個通用視圖都需要知道它將作用於什麼模型。這是使用model屬性提供的。
DetailView通用視圖期望將從URL捕獲的主鍵值稱爲“ pk”,因此我們已將通用視圖的question_id更改爲pk。
默認情況下,DetailView通用視圖使用名爲<應用程序名稱> / <模型名稱> _detail.html的模板。在我們的例子中,它將使用模板“ polls / question_detail.html”。 template_name屬性用於告訴Django使用特定的模板名稱,而不是自動生成的默認模板名稱。我們還爲結果列表視圖指定了template_name,這可以確保結果視圖和局部視圖在呈現時具有不同的外觀,即使它們都是後臺的DetailView。
同樣,ListView通用視圖使用一個名爲<app name> / <model name> _list.html的默認模板;我們使用template_name告訴ListView使用我們現有的“ polls / index.html”模板。
在本教程的前面部分中,已爲模板提供了一個上下文,該上下文包含question和latest_question_list上下文變量。對於DetailView,將自動提供question變量-由於我們使用的是Django模型(question),因此Django可以爲上下文變量確定適當的名稱。但是,對於ListView,自動生成的上下文變量是question_list。要覆蓋此內容,我們提供了context_object_name屬性,並指定我們要使用latest_question_list。作爲一種替代方法,您可以更改模板以匹配新的默認上下文變量,但是告訴Django使用所需的變量要容易得多。
修改完後檢查運行效果。效果還是一樣,只是代碼簡單了。
本章內容就到此爲止。如果對錶單form 和通用視圖view 比較有感覺了,可以進入下一章:python 的web框架 django 的入門教程 5