在本教程中,我們將向您展示如何在Django中使用HTML表單,特別是編寫表單以創(chuàng)建,更新和刪除模型實例的最簡單方法。作為本演示的一部分,我們將擴展LocalLibrary網(wǎng)站,以便圖書館員可以使用我們自己的表單(而不是使用管理員應(yīng)用程序)更新圖書,創(chuàng)建,更新和刪除作者。 一張 HTML 表單 ,是由一個或多個欄位/widget在一個網(wǎng)頁上組成的,以用于向使用者收集資料,并提交至伺服器。表單是一個彈性的機制,用于收集使用者輸入,有合適的 widgets 可輸入許多不同型態(tài)的資料,包含文字框、復(fù)選框、單選按鈕、日期選取組件等等。若是允許我們用 POST 方式傳送資料,并附加 CSRF 跨站要求偽造保護(hù),表單也是與伺服器分享資料的一種相對安全的方式。 在這個教程目前為止,我們還沒有創(chuàng)造任何表單,但我們已經(jīng)在 Django 管理站點遇到這些表單了— 例如以下的擷圖展示了一張表單,用于編輯我們的一個 Book書本模型,包含一些選擇列表以及文字編輯框。 表單的使用可以很復(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表單??紤]一個簡單的HTML表單,其中包含一個文本字段,用于輸入某些“團(tuán)隊”的名稱及其相關(guān)標(biāo)簽: 表單在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 屬性,定義將顯示哪種窗口小部件。該字段的名稱name 和 id ,用于標(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 ): 服務(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 如何處理表單請求的流程圖,從對包含表單的頁面的請求開始(以綠色顯示)。 基于上圖,Django 表單處理的主要內(nèi)容是: 在用戶第一次請求時,顯示默認(rèn)表單。 從提交請求接收數(shù)據(jù),并將其綁定到表單。 清理并驗證數(shù)據(jù)。 如果任何數(shù)據(jù)無效,請重新顯示表單,這次使用任何用戶填充的值,和問題字段的錯誤消息。 如果所有數(shù)據(jù)都有效,請執(zhí)行必要的操作(例如保存數(shù)據(jù),發(fā)送表單和發(fā)送電子郵件,返回搜索結(jié)果,上傳文件等) 完成所有操作后,將用戶重定向到另一個頁面。
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 項目所做的更改。 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ù)制到其中。 在創(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 的縮寫,是一個合理的慣例! 正如上面的 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() 中的 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 友好類型。 視圖的表單處理部分的最后一步,是重定向到另一個頁面,通常是“成功”頁面。在這種情況下,我們使用 HttpResponseRedirect 和 reverse() ,重定向到名為'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}) 創(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文檔)。 如果您接受了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 字段)。 如果您成功,默認(rèn)表單將如下所示: 輸入無效值的表單將如下所示: 所有包含續(xù)借鏈接的圖書清單如下所示: 使用上述方法創(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)入和使用它。 我們在上面的函數(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)(這可能很有用,如果您需要比管理站點能提供的、更加靈活的管理功能)。 打開視圖文件(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 查看屬性。
“創(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 配置文件(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)限)。 首先,使用具有訪問作者編輯頁面權(quán)限的帳戶(由您決定),登錄該站點。 然后導(dǎo)航到作者創(chuàng)建頁面: http://127.0.0.1:8000/catalog/author/create/,它應(yīng)該如下面的截圖。 輸入字段的值,然后按“提交” 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)。刪除記錄,并將其帶到所有作者的列表中。 創(chuàng)建一些表單,來創(chuàng)建、編輯和刪除書本記錄Book 。您可以使用與作者Authors 完全相同的結(jié)構(gòu)。如果您的 book_form.html 模板只是 author_form.html 模板的復(fù)制重命名版本,則新的“創(chuàng)建圖書”頁面,將如下所示: 創(chuàng)建和處理表單可能是一個復(fù)雜的過程! Django通過提供聲明、呈現(xiàn)和驗證表單的編程機制,使其變得更加容易。此外,Django提供了通用的表單編輯視圖,幾乎可以完成所有工作,以定義可以創(chuàng)建,編輯和刪除與單個模型實例關(guān)聯(lián)的記錄的頁面。 表單可以完成更多工作(請參閱下面的“請參閱”列表),但您現(xiàn)在應(yīng)該了解,如何將基本表單和表單處理代碼,添加到您自己的網(wǎng)站。
|