python 的web框架 django 的入門教程 4 表單

本文是在 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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章