一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Django 教程 9: 使用表單

 寧靜致遠(yuǎn)oj1kn5 2019-06-13

在本教程中,我們將向您展示如何在Django中使用HTML表單,特別是編寫表單以創(chuàng)建,更新和刪除模型實例的最簡單方法。作為本演示的一部分,我們將擴展LocalLibrary網(wǎng)站,以便圖書館員可以使用我們自己的表單(而不是使用管理員應(yīng)用程序)更新圖書,創(chuàng)建,更新和刪除作者。

前置條件:完成所有先前的教程主題,包含 Django 教程 8: 使用者授權(quán)與許可
目標(biāo):了解如何撰寫表單,向使用者取得資料,并更新資料庫。了解通用類別表單編輯視圖,如何大量地簡化用于單一模型的新表單制作。

概覽節(jié)

一張 HTML 表單 ,是由一個或多個欄位/widget在一個網(wǎng)頁上組成的,以用于向使用者收集資料,并提交至伺服器。表單是一個彈性的機制,用于收集使用者輸入,有合適的 widgets 可輸入許多不同型態(tài)的資料,包含文字框、復(fù)選框、單選按鈕、日期選取組件等等。若是允許我們用 POST 方式傳送資料,并附加 CSRF 跨站要求偽造保護(hù),表單也是與伺服器分享資料的一種相對安全的方式。

在這個教程目前為止,我們還沒有創(chuàng)造任何表單,但我們已經(jīng)在 Django 管理站點遇到這些表單了— 例如以下的擷圖展示了一張表單,用于編輯我們的一個 Book書本模型,包含一些選擇列表以及文字編輯框。

Admin Site - Book Add

表單的使用可以很復(fù)雜!開發(fā)者需要為表單撰寫 HTML 語法,在服務(wù)端驗證輸入的資料并經(jīng)過充分的安全處理(并且可能在瀏覽器端也需要),回到表單呈現(xiàn)錯誤信息,告知使用者任何無效的欄位,當(dāng)成功提交時處理資料,在最后用某些方式回應(yīng)使用者表單提交成功的信息。經(jīng)由提供一個框架,讓你程序化定義表單以及其中的欄位,Django 表單接手處理了以上這些步驟的大量工作,比如使用這些物件,產(chǎn)生表單的 HTML 源碼,并處理大量的驗證、使用者互動的工作。

在本教程中,我們將展示一些方法,用以創(chuàng)造并使用表單,特別是,當(dāng)你創(chuàng)造用以操作資料模型的表單,通用編輯表單視圖如何顯著降低你的工作量。在此過程中,我們將通過添加表單,來擴展我們的 LocalLibrary 應(yīng)用程序,以允許圖書館員更新圖書館書本,我們將創(chuàng)建頁面來創(chuàng)建,編輯和刪除書本和作者(復(fù)制上面顯示的表格的基本版本,以便編輯書本)。

HTML 表單節(jié)

首先簡要概述HTML表單??紤]一個簡單的HTML表單,其中包含一個文本字段,用于輸入某些“團(tuán)隊”的名稱及其相關(guān)標(biāo)簽:

Simple name field example in HTML form

表單在HTML中定義為<form>...</form> 標(biāo)記內(nèi)的元素集合,包含至少一個type="submit"input 輸入元素。

<form action="/team_name_url/" method="post">
    <label for="team_name">Enter name: </label>
    <input id="team_name" type="text" name="name_field" value="Default name for team.">
    <input type="submit" value="OK">
</form>

雖然在這里,我們只有一個文本字段,用于輸入團(tuán)隊名稱,但表單可能包含任意數(shù)量的其他輸入元素,及其相關(guān)標(biāo)簽。字段的type 屬性,定義將顯示哪種窗口小部件。該字段的名稱nameid,用于標(biāo)識 JavaScript / CSS / HTML中的字段,而value 定義字段首次顯示時的初始值。匹配團(tuán)隊標(biāo)簽使用label 標(biāo)簽指定(請參閱上面的“輸入名稱” Enter name),其中for 字段包含相關(guān)input輸入的id 值。

提交輸入submit 將顯示為一個按鈕(默認(rèn)情況下),用戶可以按下該按鈕,將表單中所有其他輸入元素中的數(shù)據(jù),上傳到服務(wù)器(在本例中,只有team_name)。表單屬性定義用于發(fā)送數(shù)據(jù)的 HTTP method 方法,和服務(wù)器上數(shù)據(jù)的目標(biāo)(action):       

  • action: 提交表單時,要發(fā)送數(shù)據(jù)以進(jìn)行處理的資源 /URL。如果未設(shè)置(或設(shè)置為空字符串),則表單將提交回當(dāng)前頁面 URL。

  • method: 用于發(fā)送數(shù)據(jù)的HTTP方法:post 或 get。

    • 如果數(shù)據(jù)將導(dǎo)致服務(wù)器數(shù)據(jù)庫的更改,則應(yīng)始終使用POST 方法,因為這可以更加抵抗跨站點偽造請求攻擊。

    • GET 方法,只應(yīng)用于不更改用戶數(shù)據(jù)的表單(例如搜索表單)。當(dāng)您希望能夠為URL添加書簽、或共享時,建議使用此選項。

服務(wù)器的角色,首先是呈現(xiàn)初始表單狀態(tài) - 包含空白字段或預(yù)先填充初始值。在用戶按下提交按鈕之后,服務(wù)器將從Web瀏覽器,接收具有值的表單數(shù)據(jù),并且必須驗證該信息。如果表單包含無效數(shù)據(jù),則服務(wù)器應(yīng)再次顯示表單,這次使用用戶輸入的數(shù)據(jù)在“有效”字段中,并使用消息來描述無效字段的問題。一旦服務(wù)器獲得具有所有有效表單數(shù)據(jù)的請求,它就可以執(zhí)行適當(dāng)?shù)牟僮鳎ɡ纾4鏀?shù)據(jù),返回搜索結(jié)果,上載文件等),然后通知用戶。

可以想象,創(chuàng)建HTML,驗證返回的數(shù)據(jù),根據(jù)需要重新顯示輸入的數(shù)據(jù),和錯誤報告,以及對有效數(shù)據(jù)執(zhí)行所需的操作,都需要花費很多精力才能“正確”。通過刪除一些繁重的重復(fù)代碼,Django 使這變得更容易!

Django 表單處理流程節(jié)

Django 的表單處理,使用了我們在之前的教程中,學(xué)到的所有相同技術(shù)(用于顯示有關(guān)模型的信息):視圖獲取請求,執(zhí)行所需的任何操作,包括從模型中讀取數(shù)據(jù),然后生成并返回HTML頁面(從模板中,我們傳遞一個包含要顯示的數(shù)據(jù)的上下文。使事情變得更復(fù)雜的是,服務(wù)器還需要能夠處理用戶提供的數(shù)據(jù),并在出現(xiàn)任何錯誤時,重新顯示頁面。

下面顯示了 Django 如何處理表單請求的流程圖,從對包含表單的頁面的請求開始(以綠色顯示)。

Updated form handling process doc.

基于上圖,Django 表單處理的主要內(nèi)容是:

  1. 在用戶第一次請求時,顯示默認(rèn)表單。

    • 表單可能包含空白字段(例如,如果您正在創(chuàng)建新記錄),或者可能預(yù)先填充了初始值(例如,如果您要更改記錄,或者具有有用的默認(rèn)初始值)。

    • 此時表單被稱為未綁定,因為它與任何用戶輸入的數(shù)據(jù)無關(guān)(盡管它可能具有初始值)。

  2. 從提交請求接收數(shù)據(jù),并將其綁定到表單。

    • 將數(shù)據(jù)綁定到表單,意味著當(dāng)我們需要重新顯示表單時,用戶輸入的數(shù)據(jù)和任何錯誤都可取用。 

  3. 清理并驗證數(shù)據(jù)。

    • 清理數(shù)據(jù)會對輸入執(zhí)行清理(例如,刪除可能用于向服務(wù)器發(fā)送惡意內(nèi)容的無效字符)并將其轉(zhuǎn)換為一致的 Python 類型。

    • 驗證檢查值是否適合該字段(例如,在正確的日期范圍內(nèi),不是太短或太長等)

  4. 如果任何數(shù)據(jù)無效,請重新顯示表單,這次使用任何用戶填充的值,和問題字段的錯誤消息。

  5. 如果所有數(shù)據(jù)都有效,請執(zhí)行必要的操作(例如保存數(shù)據(jù),發(fā)送表單和發(fā)送電子郵件,返回搜索結(jié)果,上傳文件等)

  6. 完成所有操作后,將用戶重定向到另一個頁面。

Django 提供了許多工具和方法,來幫助您完成上述任務(wù)。最基本的是 Form 類,它簡化了表單 HTML 和數(shù)據(jù)清理/驗證的生成。在下一節(jié)中,我們將描述表單如何使用頁面的實際示例,來允許圖書館員更新書本籍。

注意: 在我們討論 Django 更“高級”的表單框架類時,了解 Form 的使用方式,將對您有所幫助。

續(xù)借表單 - 使用表單和功能視圖節(jié)

接下來,我們將添加一個頁面,以允許圖書館員,為被借用的書本辦理續(xù)借。為此,我們將創(chuàng)建一個允許用戶輸入日期值的表單。我們將從當(dāng)前日期(正常借用期)起 3 周內(nèi),為該字段設(shè)定初始值,并添加一些驗證,以確保圖書管理員無法輸入過去的日期、或未來的日期。輸入有效日期后,我們會將其寫入當(dāng)前記錄的 BookInstance.due_back 字段。

該示例將使用基于函數(shù)的視圖和Form 類。以下部分,說明了表單的工作方式,以及您需要對正在進(jìn)行的 LocalLibrary 項目所做的更改。

表單節(jié)

Form 類是 Django 表單處理系統(tǒng)的核心。它指定表單中的字段、其布局、顯示窗口小部件、標(biāo)簽、初始值、有效值,以及(一旦驗證)與無效字段關(guān)聯(lián)的錯誤消息。該類還提供了使用預(yù)定義格式(表,列表等)在模板中呈現(xiàn)自身的方法,或者用于獲取任何元素的值(啟用細(xì)粒度手動呈現(xiàn))的方法。

聲明表單

Form 的聲明語法,與聲明Model非常相似,并且共享相同的字段類型(以及一些類似的參數(shù))。這是有道理的,因為在這兩種情況下,我們都需要確保每個字段處理正確類型的數(shù)據(jù),受限于有效數(shù)據(jù),并具有顯示/文檔的描述。

要創(chuàng)建表單,我們導(dǎo)入表單庫,從Form 類派生,并聲明表單的字段。我們的圖書館圖書續(xù)借表單的一個非?;镜谋韱晤惾缦滤荆?/p>

from django import forms
    
class RenewBookForm(forms.Form):
    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")

表單字段

在這種情況下,我們有一個 DateField 用于輸入續(xù)借日期,該日期將使用空白值在 HTML 中呈現(xiàn),默認(rèn)標(biāo)簽為“續(xù)借日期:”,以及一些有用的用法文本:“輸入從現(xiàn)在到 4 周之間的日期(默認(rèn)為 3)周)?!?nbsp; 由于沒有指定其他可選參數(shù),該字段將使用 input_formats 接受日期:YYYY-MM-DD(2016-11-06)、MM/DD/YYYY(02/26/2016)、MM/DD/YY( 10/25/16),并且將使用默認(rèn)小部件呈現(xiàn):DateInput。

還有許多其他類型的表單字段,您可以從它們與等效模型字段類的相似性中大致認(rèn)識到:

BooleanField, CharField, ChoiceField, TypedChoiceField, DateField, DateTimeField, DecimalField, DurationField, EmailField, FileField, FilePathField, FloatField, ImageField, IntegerField, GenericIPAddressField, MultipleChoiceField, TypedMultipleChoiceField, NullBooleanField, RegexField, SlugField, TimeField, URLField, UUIDField, ComboField, MultiValueField, SplitDateTimeField, ModelMultipleChoiceField, ModelChoiceField.

下面列出了大多數(shù)字段共有的參數(shù)(這些參數(shù)具有合理的默認(rèn)值):

  • required: 如果為True,則該字段不能留空或給出None值。默認(rèn)情況下需要字段,因此您可以設(shè)置required=False以允許表單中的空白值。

  • label: 在 HTML 中呈現(xiàn)字段時使用的標(biāo)簽。如果未指定label,則 Django 將通過大寫第一個字母、并用空格替換下劃線(例如續(xù)訂日期)的方式,從字段名稱創(chuàng)建一個。

  • label_suffix: 默認(rèn)情況下,標(biāo)簽后面會顯示冒號(例如續(xù)借日期:)。此參數(shù)允許您指定包含其他字符的不同后綴。

  • initial: 顯示表單時,字段的初始值。

  • widget: 要使用的顯示小部件。

  • help_text (如上例所示):可以在表單中顯示的附加文本,用于說明如何使用該字段。

  • error_messages: 字段的錯誤消息列表。如果需要,您可以使用自己的消息,覆蓋這些消息。

  • validators: 驗證時將在字段上調(diào)用的函數(shù)列表。

  • localize: 啟用表單數(shù)據(jù)輸入的本地化(有關(guān)詳細(xì)信息,請參閱鏈接)。

  • disabled: 如果為True,該字段會被顯示,但無法編輯其值。默認(rèn)值為False。

驗證

Django 提供了許多可以驗證數(shù)據(jù)的地方。驗證單個字段的最簡單方法,是覆蓋要檢查的字段的方法clean_<fieldname>() 。因此,例如,我們可以通過實現(xiàn)clean_renewal_date()  ,驗證輸入的renewal_date 值是從現(xiàn)在到 4 周之間,如下所示。

from django import formsfrom django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
import datetime #for checking renewal date range.    
class RenewBookForm(forms.Form):
    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")    def clean_renewal_date(self):
        data = self.cleaned_data['renewal_date']
        
        #Check date is not in past. 
        if data < datetime.date.today():
            raise ValidationError(_('Invalid date - renewal in past'))

        #Check date is in range librarian allowed to change (+4 weeks).
        if data > datetime.date.today() + datetime.timedelta(weeks=4):
            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))

        # Remember to always return the cleaned data.
        return data

有兩件重要的事情需要注意。首先,我們使用self.cleaned_data['renewal_date'] 獲取數(shù)據(jù),并且無論是否在函數(shù)末尾更改數(shù)據(jù),我們都會返回此數(shù)據(jù)。此步驟使用默認(rèn)驗證器,將數(shù)據(jù)“清理”、并清除可能不安全的輸入,并轉(zhuǎn)換為數(shù)據(jù)的正確標(biāo)準(zhǔn)類型(在本例中為Python datetime.datetime對象)。

第二點是,如果某個值超出了我們的范圍,我們會引發(fā)ValidationError,指定在輸入無效值時,我們要在表單中顯示的錯誤文本。上面的例子,也將這個文本包含在 Django 的翻譯函數(shù)ugettext_lazy()中(導(dǎo)入為 _()),如果你想在稍后翻譯你的網(wǎng)站,這是一個很好的做法。

注意:  在表單和字段驗證(Django docs)中驗證表單還有其他很多方法和示例。例如,如果您有多個相互依賴的字段,則可以覆蓋Form.clean() 函數(shù)并再次引發(fā)ValidationError。

這就是我們在這個例子中,對表單所需要了解的全部內(nèi)容!

復(fù)制表單

創(chuàng)建并打開文件 locallibrary/catalog/forms.py,并將前一個塊中的整個代碼清單,復(fù)制到其中。

URL 配置節(jié)

在創(chuàng)建視圖之前,讓我們?yōu)槔m(xù)借頁面添加 URL 配置。將以下配置,復(fù)制到locallibrary/catalog/urls.py 的底部。

urlpatterns += [   
    path('book/<uuid:pk>/renew/', views.renew_book_librarian, name='renew-book-librarian'),
]

URL 配置會將格式為 /catalog/book/<bookinstance id>/renew/的URL,重定向到 views.py 中,名為renew_book_librarian() 的函數(shù),并將BookInstance id作為名為 pk的參數(shù)發(fā)送。只有 pk是正確格式化的 uuid,該模式才會匹配。

注意:  我們可以將捕獲的 URL 數(shù)據(jù),命名為“pk”,因為我們可以完全控制視圖函數(shù)(我們不使用需要具有特定名稱的參數(shù)的通用詳細(xì)視圖類)。然而,pk,“主鍵” primary key 的縮寫,是一個合理的慣例!

視圖節(jié)

正如上面的 Django 表單處理過程中,所討論的那樣,視圖必須在首次調(diào)用時呈現(xiàn)默認(rèn)表單,然后在數(shù)據(jù)無效時,重新呈現(xiàn)它,并顯示錯誤消息,或者數(shù)據(jù)有效時,處理數(shù)據(jù),并重定向到新頁面。為了執(zhí)行這些不同的操作,視圖必須能夠知道,它是第一次被調(diào)用以呈現(xiàn)默認(rèn)表單,還是后續(xù)處理以驗證數(shù)據(jù)。

對于使用POST 請求向服務(wù)器提交信息的表單,最常見的模式,是視圖針對POST 請求類型進(jìn)行測試(if request.method == 'POST':)以識別表單驗證請求和GET (使用一個else 條件)來識別初始表單創(chuàng)建請求。如果要使用GET 請求提交數(shù)據(jù),則識別這是第一個、還是后續(xù)視圖調(diào)用的典型方法,是讀取表單數(shù)據(jù)(例如,讀取表單中的隱藏值)。

書本續(xù)借過程將寫入我們的數(shù)據(jù)庫,因此按照慣例,我們使用 POST 請求方法。下面的代碼片段,顯示了這種函數(shù)視圖的(非常標(biāo)準(zhǔn))模式。

from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
import datetime

from .forms import RenewBookForm

def renew_book_librarian(request, pk):
    book_inst=get_object_or_404(BookInstance, pk = pk)

    # If this is a POST request then process the Form data    if request.method == 'POST':        # Create a form instance and populate it with data from the request (binding):
        form = RenewBookForm(request.POST)

        # Check if the form is valid:
        if form.is_valid():            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
            book_inst.due_back = form.cleaned_data['renewal_date']
            book_inst.save()

            # redirect to a new URL:
            return HttpResponseRedirect(reverse('all-borrowed') )

    # If this is a GET (or any other method) create the default form.    else:        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})

    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})

首先,我們導(dǎo)入我們的表單(RenewBookForm)和視圖函數(shù)中使用的許多其他有用的對象/方法:

  • get_object_or_404():  根據(jù)模型的主鍵值,從模型返回指定的對象,如果記錄不存在,則引發(fā)Http404 異常(未找到)。

  • HttpResponseRedirect:  這將創(chuàng)建指向指定URL的重定向(HTTP狀態(tài)代碼 302)。

  • reverse():  這將從 URL 配置名稱和一組參數(shù)生成 URL。它是我們在模板中使用的 url 標(biāo)記的 Python 等價物。

  • datetime:  用于操作日期和時間的 Python 庫。

在視圖中,我們首先使用 get_object_or_404()中的 pk 參數(shù),來獲取當(dāng)前的 BookInstance (如果這不存在,視圖將立即退出,頁面將顯示“未找到”錯誤)。如果這不是 POST 請求(由 else 子句處理),那么我們創(chuàng)建默認(rèn)表單,傳遞 renewal_date 字段的initial 初始值(如下面的粗體所示,這是從當(dāng)前日期起的 3 周)。

book_inst=get_object_or_404(BookInstance, pk = pk)    

    # If this is a GET (or any other method) create the default form
    else:        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})

創(chuàng)建表單后,我們調(diào)用 render() 來創(chuàng)建HTML頁面,指定模板和包含表單的上下文。在這種情況下,上下文還包含我們的 BookInstance,我們將在模板中使用它,來提供有關(guān)我們正在續(xù)借的書本信息。

但是,如果這是一個POST 請求,那么我們創(chuàng)建表單對象,并使用請求中的數(shù)據(jù)填充它。此過程稱為“綁定”,并且允許我們驗證表單。然后我們檢查表單是否有效,它運行所有字段上的所有驗證代碼 - 包括用于檢查我們的日期字段,實際上是有效日期的通用代碼,以及用于檢查日期的特定表單的clean_renewal_date()函數(shù)在合適的范圍內(nèi)。

    book_inst=get_object_or_404(BookInstance, pk = pk)

    # If this is a POST request then process the Form data
    if request.method == 'POST':

        # Create a form instance and populate it with data from the request (binding):        form = RenewBookForm(request.POST)        # Check if the form is valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
            book_inst.due_back = form.cleaned_data['renewal_date']
            book_inst.save()

            # redirect to a new URL:
            return HttpResponseRedirect(reverse('all-borrowed') )

    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})

如果表單無效,我們再次調(diào)用render() ,但這次在上下文中傳遞的表單值將包含錯誤消息。

如果表單有效,那么我們可以開始使用數(shù)據(jù),通過 form.cleaned_data屬性訪問它(例如 data = form.cleaned_data['renewal_date'])。這里我們只將數(shù)據(jù)保存到關(guān)聯(lián)的BookInstance 對象的due_back 值中。

重要:  雖然您也可以通過請求直接訪問表單數(shù)據(jù)(例如request.POST['renewal_date']request.GET['renewal_date'](如果使用 GET 請求),但不建議這樣做。清理后的數(shù)據(jù)是無害的、驗證過的、并轉(zhuǎn)換為 Python 友好類型。

視圖的表單處理部分的最后一步,是重定向到另一個頁面,通常是“成功”頁面。在這種情況下,我們使用 HttpResponseRedirectreverse() ,重定向到名為'all-borrowed'的視圖(這是在 Django 教程第 8 部分中創(chuàng)建的 “挑戰(zhàn)”:用戶身份驗證和權(quán)限)。如果您沒有創(chuàng)建該頁面,請考慮重定向到URL'/'處的主頁。

這就是表單處理本身所需的一切,但我們?nèi)匀恍枰獙⒁晥D,限制為圖書館員可以訪問。我們應(yīng)該在 BookInstance (“can_renew”)中創(chuàng)建一個新的權(quán)限,但為了簡單起見,我們只需使用@permission_required函數(shù)裝飾器,和我們現(xiàn)有的 can_mark_returned 權(quán)限。

因此,最終視圖如下所示。請將其復(fù)制到 locallibrary/catalog/views.py 的底部。

from django.contrib.auth.decorators import permission_requiredfrom django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
import datetime

from .forms import RenewBookForm@permission_required('catalog.can_mark_returned')def renew_book_librarian(request, pk):
    """
    View function for renewing a specific BookInstance by librarian
    """
    book_inst=get_object_or_404(BookInstance, pk = pk)

    # If this is a POST request then process the Form data
    if request.method == 'POST':

        # Create a form instance and populate it with data from the request (binding):
        form = RenewBookForm(request.POST)

        # Check if the form is valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
            book_inst.due_back = form.cleaned_data['renewal_date']
            book_inst.save()

            # redirect to a new URL:
            return HttpResponseRedirect(reverse('all-borrowed') )

    # If this is a GET (or any other method) create the default form.
    else:
        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})

    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})

模板節(jié)

創(chuàng)建視圖中引用的模板(/catalog/templates/catalog/book_renew_librarian.html),并將下面的代碼,復(fù)制到其中:

{% extends "base_generic.html" %}
{% block content %}

    <h1>Renew: {{bookinst.book.title}}</h1>
    <p>Borrower: {{bookinst.borrower}}</p>
    <p{% if bookinst.is_overdue %} class="text-danger"{% endif %}>Due date: {{bookinst.due_back}}</p>
    
    <form action="" method="post">
        {% csrf_token %}
        <table>
        {{ form }}
        </table>
        <input type="submit" value="Submit" />
    </form>

{% endblock %}

這里大部分內(nèi)容,和以前的教程都是完全類似的。我們擴展基本模板,然后重新定義內(nèi)容塊。我們能夠引用 {{bookinst}}(及其變量),因為它被傳遞到 render()函數(shù)中的上下文對象中,我們使用這些來列出書名,借閱者和原始截止日期。

表單代碼相對簡單。首先,我們聲明表單標(biāo)簽,指定表單的提交位置(action)和提交數(shù)據(jù)的方法(在本例中為 “HTTP POST”) - 如果您回想一下頁面頂部的 HTML 表單概述,如圖所示的空action ,意味著表單數(shù)據(jù)將被發(fā)布回頁面的當(dāng)前 URL(這是我們想要的?。T跇?biāo)簽內(nèi)部,我們定義了submit 提交輸入,用戶可以按這個輸入來提交數(shù)據(jù)。在表單標(biāo)簽內(nèi)添加的{% csrf_token %} ,是 Django 跨站點偽造保護(hù)的一部分。

注意:  將{% csrf_token %} 添加到您創(chuàng)建的每個使用 POST 提交數(shù)據(jù)的 Django 模板中。這將減少惡意用戶劫持表單的可能性。

剩下的就是 {{form}}模板變量,我們將其傳遞給上下文字典中的模板。也許不出所料,當(dāng)如圖所示使用時,它提供了所有表單字段的默認(rèn)呈現(xiàn),包括它們的標(biāo)簽、小部件、和幫助文本 - 呈現(xiàn)如下所示:

<tr>
  <th><label for="id_renewal_date">Renewal date:</label></th>
  <td>
    <input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required />
    <br />
    <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span>
  </td>
</tr>

注意:   它可能并不明顯,因為我們只有一個字段,但默認(rèn)情況下,每個字段都在其自己的表格行中定義(這就是變量在上面的table 表格標(biāo)記內(nèi)部的原因)。如果您引用模板變量{{ form.as_table }},會提供相同的渲染。

如果您輸入無效日期,您還會獲得頁面中呈現(xiàn)的錯誤列表(下面以粗體顯示)。

<tr>
  <th><label for="id_renewal_date">Renewal date:</label></th>
   <td>
      <ul class="errorlist">
        <li>Invalid date - renewal in past</li>
      </ul>
      <input id="id_renewal_date" name="renewal_date" type="text" value="2015-11-08" required />
      <br />
      <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span>
    </td>
</tr>

使用表單模板變量的其他方法

如上所示使用{{form}},每個字段都呈現(xiàn)為表格行。您還可以將每個字段呈現(xiàn)為列表項(使用{{form.as_ul}} )或作為段落(使用{{form.as_p}})。

更酷的是,您可以通過使用點表示法,索引其屬性,來完全控制表單每個部分的呈現(xiàn)。例如,我們可以為renewal_date 字段訪問許多單獨的項目:

  • {{form.renewal_date}}: 整個領(lǐng)域。

  • {{form.renewal_date.errors}}: 錯誤列表。

  • {{form.renewal_date.id_for_label}}: 標(biāo)簽的 id 。

  • {{form.renewal_date.help_text}}: 字段幫助文本。

  •  其他等等!

有關(guān)如何在模板中,手動呈現(xiàn)表單,并動態(tài)循環(huán)模板字段的更多示例,請參閱使用表單>手動呈現(xiàn)字段(Django文檔)。

測試頁面節(jié)

如果您接受了Django 教程第 8 部分中的 “挑戰(zhàn)”:用戶身份驗證和權(quán)限,您將獲得圖書館中借出的所有書本的列表,這只有圖書館工作人員才能看到。我們可以使用下面的模板代碼,為每個項目旁邊的續(xù)借頁面,添加鏈接。

{% if perms.catalog.can_mark_returned %}- <a href="{% url 'renew-book-librarian' bookinst.id %}">Renew</a>  {% endif %}

注意:  請記住,您的測試登錄需要具有“catalog.can_mark_returned”權(quán)限,才能訪問續(xù)借書本頁面(可能使用您的超級用戶帳戶)。

您也可以手動構(gòu)建這樣的測試URL  -  http://127.0.0.1:8000/catalog/book/<bookinstance_id>/renew/ (可以通過導(dǎo)航到圖書館中的書本詳細(xì)信息頁面,獲取有效的 bookinstance id,并復(fù)制id 字段)。

它看起來是什么樣子?節(jié)

如果您成功,默認(rèn)表單將如下所示:

輸入無效值的表單將如下所示:

所有包含續(xù)借鏈接的圖書清單如下所示:

模型表單節(jié)

使用上述方法創(chuàng)建Form 類非常靈活,允許您創(chuàng)建任何類型的表單頁面,并將其與任何單一模型、或多個模型相關(guān)聯(lián)。

但是,如果您只需要一個表單,來映射單個模型的字段,那么您的模型,將已經(jīng)定義了表單中所需的大部分信息:字段、標(biāo)簽、幫助文本等。而不是在表單中重新創(chuàng)建模型定義,使用 ModelForm 幫助程序類從模型創(chuàng)建表單更容易。然后,可以在視圖中使用此ModelForm ,其方式與普通Form完全相同。

包含與原始RenewBookForm 相同的字段的基本 ModelForm 如下所示。創(chuàng)建表單所需要做的,就是添加帶有相關(guān)模型(BookInstance)的class Meta、和要包含在表單中的模型字段列表(您可以使用 fields = '__all__',以包含所有字段,或者您可以使用 exclude (而不是字段),指定不包含在模型中的字段)。

from django.forms import ModelForm
from .models import BookInstance

class RenewBookModelForm(ModelForm):    class Meta:
        model = BookInstance
        fields = ['due_back',]

注意:  這可能看起來不像使用Form 那么簡單(在這種情況下不是這樣,因為我們只有一個字段)。但是,如果你有很多字段,它可以顯著減少代碼量!

其余信息來自模型字段的定義(例如標(biāo)簽、小部件、幫助文本、錯誤消息)。如果這些不太正確,那么我們可以在 Meta類中覆蓋它們,指定包含要更改的字段、及其新值的字典。例如,在這種形式中,我們可能需要 “更新日期” Renewal date 字段的標(biāo)簽(而不是基于字段名稱的默認(rèn)值:截止日期 Due date),并且我們還希望我們的幫助文本,特定于此用例。下面的Meta 顯示了如何覆蓋這些字段,如果默認(rèn)值不夠,您可以類似地方式設(shè)置widgets 窗口小部件和error_messages 。

class Meta:
    model = BookInstance
    fields = ['due_back',]    labels = { 'due_back': _('Renewal date'), }
    help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), } 

要添加驗證,您可以使用與普通表單相同的方法 - 定義名為 clean_field_name()的函數(shù),并為無效值引發(fā)ValidationError 異常。與我們原始形式的唯一區(qū)別,是模型字段名為due_back 而不是“renewal_date”。

from django.forms import ModelForm
from .models import BookInstance

class RenewBookModelForm(ModelForm):    def clean_due_back(self):
       data = self.cleaned_data['due_back']
       
       #Check date is not in past.
       if data < datetime.date.today():
           raise ValidationError(_('Invalid date - renewal in past'))

       #Check date is in range librarian allowed to change (+4 weeks)
       if data > datetime.date.today() + datetime.timedelta(weeks=4):
           raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))

       # Remember to always return the cleaned data.
       return data
    class Meta:
        model = BookInstance
        fields = ['due_back',]
        labels = { 'due_back': _('Renewal date'), }
        help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), }

下面的 RenewBookModelForm 類現(xiàn)在在功能上等同于我們原來的 RenewBookForm。您可以在當(dāng)前使用RenewBookForm 的任何地方導(dǎo)入和使用它。

通用編輯視圖節(jié)

我們在上面的函數(shù)視圖示例中,使用的表單處理算法,表示表單編輯視圖中非常常見的模式。 Django 通過創(chuàng)建基于模型創(chuàng)建、編輯和刪除視圖的通用編輯視圖,為您抽象出大部分“樣板”。這些不僅處理“視圖”行為,而且它們會自動從模型中為您創(chuàng)建表單類(ModelForm)。

注意:  除了這里描述的編輯視圖之外,還有一個 FormView 類,它位于我們的函數(shù)視圖,和其他通用視圖之間的 “靈活性” 與 “編碼工作” 之間。使用 FormView ,您仍然需要創(chuàng)建表單,但不必實現(xiàn)所有標(biāo)準(zhǔn)表單處理模式。相反,您只需提供一個函數(shù)的實現(xiàn),一旦知道提交有效,就會調(diào)用該函數(shù)。

在本節(jié)中,我們將使用通用編輯視圖,來創(chuàng)建頁面,以添加從我們的庫中創(chuàng)建、編輯和刪除Author 作者記錄的功能 - 有效地提供管理站點一部分的基本重新實現(xiàn)(這可能很有用,如果您需要比管理站點能提供的、更加靈活的管理功能)。

視圖節(jié)

打開視圖文件(locallibrary/catalog/views.py),并將以下代碼塊,附加到其底部:

from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Author

class AuthorCreate(CreateView):
    model = Author
    fields = '__all__'
    initial={'date_of_death':'05/01/2018',}

class AuthorUpdate(UpdateView):
    model = Author
    fields = ['first_name','last_name','date_of_birth','date_of_death']

class AuthorDelete(DeleteView):
    model = Author
    success_url = reverse_lazy('authors')

如您所見,要創(chuàng)建視圖,您需要從CreateView, UpdateView, 和 DeleteView(分別)派生,然后定義關(guān)聯(lián)的模型。

對于 “創(chuàng)建” 和 “更新” 的情況,您還需要指定要在表單中顯示的字段(使用與ModelForm相同的語法)。在這種情況下,我們將說明兩者的語法,如何顯示 “所有” 字段,以及如何單獨列出它們。您還可以使用 field_name / value對的字典,為每個字段指定初始值(此處我們?yōu)榱搜菔灸康?,而任意設(shè)置死亡日期 - 您可能希望刪除它?。?。默認(rèn)情況下,這些視圖會在成功時,重定向到顯示新創(chuàng)建/編輯的模型項的頁面,在我們的示例中,這將是我們在上一個教程中,創(chuàng)建的作者詳細(xì)信息視圖。您可以通過顯式聲明參數(shù)success_url  ,指定備用重定向位置(與AuthorDelete 類一樣)。

AuthorDelete 類不需要顯示任何字段,因此不需要指定這些字段。但是你需要指定success_url,因為 Django 沒有明顯的默認(rèn)值。在這種情況下,我們使用reverse_lazy()函數(shù),在刪除作者后,重定向到我們的作者列表 -  reverse_lazy()是一個延遲執(zhí)行的reverse()版本,在這里使用,是因為我們提供了一個基于類的 URL 查看屬性。

模板節(jié)

“創(chuàng)建” 和 “更新” 視圖默認(rèn)使用相同的模板,它將以您的模型命名:model_name_form.html(您可以使用視圖中的template_name_suffix 字段,將后綴更改為_form 以外的其他內(nèi)容,例如,template_name_suffix = '_other_suffix'

創(chuàng)建模板文件 locallibrary/catalog/templates/catalog/author_form.html,并復(fù)制到下面的文本中。

{% extends "base_generic.html" %}

{% block content %}

<form action="" method="post">
    {% csrf_token %}
    <table>
    {{ form.as_table }}
    </table>
    <input type="submit" value="Submit" />
    
</form>
{% endblock %}

這與我們之前的表單類似,并使用表單呈現(xiàn)字段。另請注意我們?nèi)绾温暶?code>{% csrf_token %},以確保我們的表單能夠抵抗 CSRF 攻擊。

“刪除”視圖需要查找以 model_name_confirm_delete.html 格式命名的模板(同樣,您可以在視圖中,使用template_name_suffix 更改后綴)。創(chuàng)建模板文件 locallibrary/catalog/templates/catalog/author_confirm_delete.html ,并復(fù)制到下面的文本中。

{% extends "base_generic.html" %}

{% block content %}

<h1>Delete Author</h1>

<p>Are you sure you want to delete the author: {{ author }}?</p>

<form action="" method="POST">
  {% csrf_token %}
  <input type="submit" action="" value="Yes, delete." />
</form>

{% endblock %}

URL配置節(jié)

打開 URL 配置文件(locallibrary/catalog/urls.py),并將以下配置,添加到文件的底部:

urlpatterns += [  
    path('author/create/', views.AuthorCreate.as_view(), name='author_create'),
    path('author/<int:pk>/update/', views.AuthorUpdate.as_view(), name='author_update'),
    path('author/<int:pk>/delete/', views.AuthorDelete.as_view(), name='author_delete'),
]

這里沒有什么特別的新東西!您可以看到視圖是類,因此必須通過.as_view()調(diào)用,并且您應(yīng)該能夠識別每種情況下的 URL 模式。我們必須使用 pk 作為捕獲的主鍵值的名稱,因為這是視圖類所期望的參數(shù)名稱。

作者的創(chuàng)建,更新和刪除頁面,現(xiàn)在已準(zhǔn)備好進(jìn)行測試(在這種情況下,我們不會將它們連接到站點側(cè)欄,盡管如果您愿意,也可以這樣做)。

注意:   敏銳的用戶會注意到,我們沒有采取任何措施,來防止未經(jīng)授權(quán)的用戶訪問這些頁面!我們將其作為練習(xí)留給您(提示:您可以使用PermissionRequiredMixin ,并創(chuàng)建新權(quán)限,或重用我們的can_mark_returned權(quán)限)。

測試頁面節(jié)

首先,使用具有訪問作者編輯頁面權(quán)限的帳戶(由您決定),登錄該站點。

然后導(dǎo)航到作者創(chuàng)建頁面: http://127.0.0.1:8000/catalog/author/create/,它應(yīng)該如下面的截圖。

Form Example: Create Author

輸入字段的值,然后按“提交” Submit  ,保存作者記錄?,F(xiàn)在,您應(yīng)該進(jìn)入新作者的詳細(xì)視圖,其 URL 為 http://127.0.0.1:8000/catalog/author/10。

您可以通過將 /update/ ,附加到詳細(xì)視圖 URL 的末尾,來測試編輯記錄(例如http://127.0.0.1:8000/catalog/author/10/update/) - 我們不顯示截圖,因為它看起來就像“創(chuàng)建”頁面!

最后,我們可以刪除頁面,方法是將刪除,附加到作者詳細(xì)信息視圖URL的末尾(例如http://127.0.0.1:8000/catalog/author/10/delete/)。 Django應(yīng)該顯示如下所示的刪除頁面。按 "是,刪除" (Yes, delete)。刪除記錄,并將其帶到所有作者的列表中。

挑戰(zhàn)自己節(jié)

創(chuàng)建一些表單,來創(chuàng)建、編輯和刪除書本記錄Book。您可以使用與作者Authors完全相同的結(jié)構(gòu)。如果您的 book_form.html 模板只是 author_form.html 模板的復(fù)制重命名版本,則新的“創(chuàng)建圖書”頁面,將如下所示:

    總結(jié)節(jié)

    創(chuàng)建和處理表單可能是一個復(fù)雜的過程! Django通過提供聲明、呈現(xiàn)和驗證表單的編程機制,使其變得更加容易。此外,Django提供了通用的表單編輯視圖,幾乎可以完成所有工作,以定義可以創(chuàng)建,編輯和刪除與單個模型實例關(guān)聯(lián)的記錄的頁面。

    表單可以完成更多工作(請參閱下面的“請參閱”列表),但您現(xiàn)在應(yīng)該了解,如何將基本表單和表單處理代碼,添加到您自己的網(wǎng)站。

    也可以參考節(jié)

    本系列教程節(jié)

      本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
      轉(zhuǎn)藏 分享 獻(xiàn)花(0

      0條評論

      發(fā)表

      請遵守用戶 評論公約

      類似文章 更多

      日韩国产精品激情一区| 精品香蕉一区二区在线| 麻豆在线观看一区二区| 日韩欧美一区二区不卡视频| 一区二区欧美另类稀缺| 日本加勒比系列在线播放| 91亚洲人人在字幕国产| 欧美性猛交内射老熟妇| 欧美日韩免费观看视频| 老司机精品视频在线免费看| 亚洲av熟女一区二区三区蜜桃| 91精品国产综合久久福利| 午夜资源在线观看免费高清| 国产一区二区三区丝袜不卡| 最近的中文字幕一区二区| 日韩国产亚洲欧美激情| 中文字幕一区久久综合| 免费大片黄在线观看日本| 中文字幕日韩一区二区不卡| 亚洲永久一区二区三区在线| 国产精品午夜一区二区三区| 不卡一区二区高清视频| 国产麻豆一线二线三线| 精品亚洲香蕉久久综合网| 欧美加勒比一区二区三区| 少妇丰满a一区二区三区| 欧美韩日在线观看一区| 亚洲一区二区亚洲日本| 国产毛片av一区二区三区小说| 国产激情一区二区三区不卡| 亚洲天堂精品1024| 东京不热免费观看日本| 日本成人三级在线播放| 国产成人精品久久二区二区| 丰满熟女少妇一区二区三区| 国产亚洲欧美一区二区| 国产亚洲精品一二三区| 亚洲淫片一区二区三区| 殴美女美女大码性淫生活在线播放 | 中文字幕在线区中文色| 精品欧美国产一二三区|