從Google的簡樸的單個搜索框,到常見的Blog評論提交表單,再到復雜的自定義數據輸入接口,HTML表單一直是交互性網站的支柱。 本章介紹如何用Django對用戶通過表單提交的數據進行訪問、有效性檢查以及其它處理。 與此同時,我們將介紹HttpRequest對象和Form對象。
我們在第三章講述View的函數時已經介紹過HttpRequest對象了,但當時并沒有講太多。 讓我們回憶下:每個view函數的第一個參數是一個HttpRequest對象,就像下面這個hello()函數:
from django.http import HttpResponse
def hello(request):
return HttpResponse("Hello world")
HttpRequest對象,比如上面代碼里的request變量,會有一些有趣的、你必須讓自己熟悉的屬性和方法,以便知道能拿它們來做些什么。 在view函數的執行過程中,你可以用這些屬性來獲取當前request的一些信息(比如,你正在加載這個頁面的用戶是誰,或者用的是什么瀏覽器)。
HttpRequest對象包含當前請求URL的一些信息:
屬性/方法 | 說明 | 舉例 |
---|---|---|
request.path | 除域名以外的請求路徑,以正斜杠開頭 | "/hello/" |
request.get_host() | 主機名(比如,通常所說的域名) | "127.0.0.1:8000" or"www.example.com" |
request.get_full_path() | 請求路徑,可能包含查詢字符串 | "/hello/?print=true" |
request.is_secure() | 如果通過HTTPS訪問,則此方法返回True, 否則返回False | True 或者 False |
在view函數里,要始終用這個屬性或方法來得到URL,而不要手動輸入。 這會使得代碼更加靈活,以便在其它地方重用。 下面是一個簡單的例子:
# BAD!
def current_url_view_bad(request):
return HttpResponse("Welcome to the page at /current/")
# GOOD
def current_url_view_good(request):
return HttpResponse("Welcome to the page at %s" % request.path)
request.META 是一個Python字典,包含了所有本次HTTP請求的Header信息,比如用戶IP地址和用戶Agent(通常是瀏覽器的名稱和版本號)。 注意,Header信息的完整列表取決于用戶所發送的Header信息和服務器端設置的Header信息。 這個字典中幾個常見的鍵值有:
HTTP_REFERER,進站前鏈接網頁,如果有的話。 (請注意,它是REFERRER的筆誤。)
HTTP_USER_AGENT,用戶瀏覽器的user-agent字符串,如果有的話。 例如:"Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17" .
注意,因為 request.META 是一個普通的Python字典,因此當你試圖訪問一個不存在的鍵時,會觸發一個KeyError異常。 (HTTP header信息是由用戶的瀏覽器所提交的、不應該給予信任的“額外”數據,因此你總是應該好好設計你的應用以便當一個特定的Header數據不存在時,給出一個優雅的回應。)你應該用 try/except 語句,或者用Python字典的 get() 方法來處理這些“可能不存在的鍵”:
# BAD!
def ua_display_bad(request):
ua = request.META['HTTP_USER_AGENT'] # Might raise KeyError!
return HttpResponse("Your browser is %s" % ua)
# GOOD (VERSION 1)
def ua_display_good1(request):
try:
ua = request.META['HTTP_USER_AGENT']
except KeyError:
ua = 'unknown'
return HttpResponse("Your browser is %s" % ua)
# GOOD (VERSION 2)
def ua_display_good2(request):
ua = request.META.get('HTTP_USER_AGENT', 'unknown')
return HttpResponse("Your browser is %s" % ua)
我們鼓勵你動手寫一個簡單的view函數來顯示 request.META 的所有數據,這樣你就知道里面有什么了。 這個view函數可能是這樣的:
def display_meta(request):
values = request.META.items()
values.sort()
html = []
for k, v in values:
html.append('%s %s ' % (k, v))
return HttpResponse('%s
' % '\n'.join(html))
做為一個練習,看你自己能不能把上面這個view函數改用Django模板系統來實現,而不是上面這樣來手動輸入HTML代碼。 也可以試著把前面提到的 request.path 方法或 HttpRequest 對象的其它方法加進去。
除了基本的元數據,HttpRequest對象還有兩個屬性包含了用戶所提交的信息: request.GET 和 request.POST。二者都是類字典對象,你可以通過它們來訪問GET和POST數據。
類字典對象
我們說“request.GET和request.POST是類字典對象”,意思是他們的行為像Python里標準的字典對象,但在技術底層上他們不是標準字典對象。 比如說,request.GET和request.POST都有get()、keys()和values()方法,你可以用用 for key in request.GET 獲取所有的鍵。
那到底有什么區別呢? 因為request.GET和request.POST擁有一些普通的字典對象所沒有的方法。 我們會稍后講到。
你可能以前遇到過相似的名字:類文件對象,這些Python對象有一些基本的方法,如read(),用來做真正的Python文件對象的代用品。
POST數據是來自HTML中的〈form〉標簽提交的,而GET數據可能來自〈form〉提交也可能是URL中的查詢字符串(the query string)。
繼續本書一直進行的關于書籍、作者、出版社的例子,我們現在來創建一個簡單的view函數以便讓用戶可以通過書名從數據庫中查找書籍。
通常,表單開發分為兩個部分: 前端HTML頁面用戶接口和后臺view函數對所提交數據的處理過程。 第一部分很簡單;現在我們來建立個view來顯示一個搜索表單:
from django.shortcuts import render_to_response
def search_form(request):
return render_to_response('search_form.html')
在第三章已經學過,這個view函數可以放到Python的搜索路徑的任何位置。 為了便于討論,咱們將它放在 books/views.py 里。
這個 search_form.html 模板,可能看起來是這樣的:
<head>
<title>Searchtitle>
head>
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
form>
body>
html>
而 urls.py 中的 URLpattern 可能是這樣的:
from mysite.books import views
urlpatterns = patterns('',
# ...
(r'^search-form/$', views.search_form),
# ...
)
(注意,我們直接將views模塊import進來了,而不是用類似 from mysite.views import search_form 這樣的語句,因為前者看起來更簡潔。 我們將在第8章講述更多的關于import的用法。)
現在,如果你運行 runserver 命令,然后訪問http://127.0.0.1:8000/search-form/,你會看到搜索界面。 非常簡單。
不過,當你通過這個form提交數據時,你會得到一個Django 404錯誤。 這個Form指向的URL /search/ 還沒有被實現。 讓我們添加第二個視圖函數并設置URL:
# urls.py
urlpatterns = patterns('',
# ...
(r'^search-form/$', views.search_form),
(r'^search/$', views.search),
# ...
)
# views.py
def search(request):
if 'q' in request.GET:
message = 'You searched for: %r' % request.GET['q']
else:
message = 'You submitted an empty form.'
return HttpResponse(message)
暫時先只顯示用戶搜索的字詞,以確定搜索數據被正確地提交給了Django,這樣你就會知道搜索數據是如何在這個系統中傳遞的。 簡而言之:
在HTML里我們定義了一個變量q。當提交表單時,變量q的值通過GET(method=”get”)附加在URL /search/上。
需要注意的是在這里明確地判斷q是否包含在request.GET中。就像上面request.META小節里面提到,對于用戶提交過來的數據,甚至是正確的數據,都需要進行過濾。 在這里若沒有進行檢測,那么用戶提交一個空的表單將引發KeyError異常:
# BAD!
def bad_search(request):
# The following line will raise KeyError if 'q' hasn't
# been submitted!
message = 'You searched for: %r' % request.GET['q']
return HttpResponse(message)
查詢字符串參數
因為使用GET方法的數據是通過查詢字符串的方式傳遞的(例如/search/?q=django),所以我們可以使用requet.GET來獲取這些數據。 第三章介紹Django的URLconf系統時我們比較了Django的簡潔的URL與PHP/Java傳統的URL,我們提到將在第七章講述如何使用傳統的URL。通過剛才的介紹,我們知道在視圖里可以使用request.GET來獲取傳統URL里的查詢字符串(例如hours=3)。
獲取使用POST方法的數據與GET的相似,只是使用request.POST代替了request.GET。那么,POST與GET之間有什么不同?當我們提交表單僅僅需要獲取數據時就可以用GET; 而當我們提交表單時需要更改服務器數據的狀態,或者說發送e-mail,或者其他不僅僅是獲取并顯示數據的時候就使用POST。 在這個搜索書籍的例子里,我們使用GET,因為這個查詢不會更改服務器數據的狀態。 (如果你有興趣了解更多關于GET和POST的知識,可以參見http://www.w3.org/2001/tag/doc/whenToUseGet.html。)
既然已經確認用戶所提交的數據是有效的,那么接下來就可以從數據庫中查詢這個有效的數據(同樣,在views.py里操作):
from django.http import HttpResponse
from django.shortcuts import render_to_response
from mysite.books.models import Book
def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
else:
return HttpResponse('Please submit a search term.')
讓我們來分析一下上面的代碼:
除了檢查q是否存在于request.GET之外,我們還檢查來reuqest.GET[‘q’]的值是否為空。
我們使用Book.objects.filter(title__icontains=q)獲取數據庫中標題包含q的書籍。 icontains是一個查詢關鍵字(參看第五章和附錄B)。這個語句可以理解為獲取標題里包含q的書籍,不區分大小寫。
這是實現書籍查詢的一個很簡單的方法。 我們不推薦在一個包含大量產品的數據庫中使用icontains查詢,因為那會很慢。 (在真實的案例中,我們可以使用以某種分類的自定義查詢系統。 在網上搜索“開源 全文搜索”看看是否有好的方法)
最后,我們給模板傳遞來books,一個包含Book對象的列表。 查詢結果的顯示模板search_results.html如下所示:
<p>You searched for: <strong>{{ query }}strong>p>
{% if books %}
<p>Found {{ books|length }} book{{ books|pluralize }}.p>
<ul>
{% for book in books %}
<li>{{ book.title }}li>
{% endfor %}
ul>
{% else %}
<p>No books matched your search criteria.p>
{% endif %}
注意這里pluralize的使用,這個過濾器在適當的時候會輸出s(例如找到多本書籍)。
同上一章一樣,我們先從最為簡單、有效的例子開始。 現在我們再來找出這個簡單的例子中的不足,然后改進他們。
首先,search()視圖對于空字符串的處理相當薄弱——僅顯示一條”Please submit a search term.”的提示信息。 若用戶要重新填寫表單必須自行點擊“后退”按鈕, 這種做法既糟糕又不專業。如果在現實的案例中,我們這樣子編寫,那么Django的優勢將蕩然無存。
在檢測到空字符串時更好的解決方法是重新顯示表單,并在表單上面給出錯誤提示以便用戶立刻重新填寫。 最簡單的實現方法既是添加else分句重新顯示表單,代碼如下:
from django.http import HttpResponse
from django.shortcuts import render_to_response
from mysite.books.models import Book
def search_form(request):
return render_to_response('search_form.html')
def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
else:
return render_to_response('search_form.html', {'error': True})
(注意,將search_form()視圖也包含進來以便查看)
這段代碼里,我們改進來search()視圖:在字符串為空時重新顯示search_form.html。 并且給這個模板傳遞了一個變量error,記錄著錯誤提示信息。 現在我們編輯一下search_form.html,檢測變量error:
<html>
<head>
<title>Searchtitle>
head>
<body>
{% if error %}
<p style="color: red;">Please submit a search term.p>
{% endif %}
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
form>
body>
html>
我們修改了search_form()視圖所使用的模板,因為search_form()視圖沒有傳遞error變量,所以在條用search_form視圖時不會顯示錯誤信息。
通過上面的一些修改,現在程序變的好多了,但是現在出現一個問題: 是否有必要專門編寫search_form()來顯示表單? 按實際情況來說,當一個請求發送至/search/(未包含GET的數據)后將會顯示一個空的表單(帶有錯誤信息)。 所以,只要我們改變search()視圖:當用戶訪問/search/并未提交任何數據時就隱藏錯誤信息,這樣就移去search_form()視圖以及對應的URLpattern。
def search(request):
error = False
if 'q' in request.GET:
q = request.GET['q']
if not q:
error = True
else:
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
return render_to_response('search_form.html',
{'error': error})
在改進后的視圖中,若用戶訪問/search/并且沒有帶有GET數據,那么他將看到一個沒有錯誤信息的表單; 如果用戶提交了一個空表單,那么它將看到錯誤提示信息,還有表單; 最后,若用戶提交了一個非空的值,那么他將看到搜索結果。
最后,我們再稍微改進一下這個表單,去掉冗余的部分。 既然已經將兩個視圖與URLs合并起來,/search/視圖管理著表單的顯示以及結果的顯示,那么在search_form.html里表單的action值就沒有必要硬編碼的指定URL。 原先的代碼是這樣:
<form action="/search/" method="get">
現在改成這樣:
<form action="" method="get">
action=”“意味著表單將提交給與當前頁面相同的URL。 這樣修改之后,如果search()視圖不指向其它頁面的話,你將不必再修改action。
我們的搜索示例仍然相當地簡單,特別從數據驗證方面來講;我們僅僅只驗證搜索關鍵值是否為空。 然后許多HTML表單包含著比檢測值是否為空更為復雜的驗證。 我們都有在網站上見過類似以下的錯誤提示信息:
請輸入一個有效的email地址, foo’ 并不是一個有效的e-mail地址。
請輸入5位數的U.S 郵政編碼, 123并非是一個有效的郵政編碼。
請輸入YYYY-MM-DD格式的日期。
關于JavaScript驗證
可以使用Javascript在客戶端瀏覽器里對數據進行驗證,這些知識已超出本書范圍。 要注意: 即使在客戶端已經做了驗證,但是服務器端仍必須再驗證一次。 因為有些用戶會將JavaScript關閉掉,并且還有一些懷有惡意的用戶會嘗試提交非法的數據來探測是否有可以攻擊的機會。
除了在服務器端對用戶提交的數據進行驗證(例如在視圖里驗證),我們沒有其他辦法。 JavaScript驗證可以看作是額外的功能,但不能作為唯一的驗證功能。
我們來調整一下search()視圖,讓她能夠驗證搜索關鍵詞是否小于或等于20個字符。 (為來讓例子更為顯著,我們假設如果關鍵詞超過20個字符將導致查詢十分緩慢)。那么該如何實現呢? 最簡單的方式就是將邏輯處理直接嵌入到視圖里,就像這樣:
def search(request):
error = False
if 'q' in request.GET:
q = request.GET['q']
if not q:
error = True
elif len(q) > 20:
error = True
else:
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
return render_to_response('search_form.html',
{'error': error})
現在,如果嘗試著提交一個超過20個字符的搜索關鍵詞,系統不會執行搜索操作,而是顯示一條錯誤提示信息。 但是,search_form.html里的這條提示信息是:”Please submit a search term.”,這顯然是錯誤的, 所以我們需要更精確的提示信息:
<html>
<head>
<title>Searchtitle>
head>
<body>
{% if error %}
<p style="color: red;">Please submit a search term 20 characters or shorter.p>
{% endif %}
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
form>
body>
html>
但像這樣修改之后仍有一些問題。 我們包含萬象的提示信息很容易使人產生困惑: 提交一個空表單怎么會出現一個關于20個字符限制的提示? 所以,提示信息必須是詳細的,明確的,不會產生疑議。
問題的實質在于我們只使用來一個布爾類型的變量來檢測是否出錯,而不是使用一個列表來記錄相應的錯誤信息。 我們需要做如下的調整:
def search(request):
errors = []
if 'q' in request.GET:
q = request.GET['q']
if not q:
errors.append('Enter a search term.')
elif len(q) > 20:
errors.append('Please enter at most 20 characters.')
else:
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
return render_to_response('search_form.html',
{'errors': errors })
接著,我們要修改一下search_form.html模板,現在需要顯示一個errors列表而不是一個布爾判斷。
<html>
<head>
<title>Searchtitle>
head>
<body>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}li>
{% endfor %}
ul>
{% endif %}
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
form>
body>
html>
雖然我們一直使用書籍搜索的示例表單,并將起改進的很完美,但是這還是相當的簡陋: 只包含一個字段,q。這簡單的例子,我們不需要使用Django表單庫來處理。 但是復雜一點的表單就需要多方面的處理,我們現在來一下一個較為復雜的例子: 站點聯系表單。
這個表單包括用戶提交的反饋信息,一個可選的e-mail回信地址。 當這個表單提交并且數據通過驗證后,系統將自動發送一封包含題用戶提交的信息的e-mail給站點工作人員。
我們從contact_form.html模板入手:
<html>
<head>
<title>Contact ustitle>
head>
<body>
<h1>Contact ush1>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}li>
{% endfor %}
ul>
{% endif %}
<form action="/contact/" method="post">
<p>Subject: <input type="text" name="subject">p>
<p>Your e-mail (optional): <input type="text" name="email">p>
<p>Message: <textarea name="message" rows="10" cols="50">textarea>p>
<input type="submit" value="Submit">
form>
body>
html>
我們定義了三個字段: 主題,e-mail和反饋信息。 除了e-mail字段為可選,其他兩個字段都是必填項。 注意,這里我們使用method=”post”而非method=”get”,因為這個表單會有一個服務器端的操作:發送一封e-mail。 并且,我們復制了前一個模板search_form.html中錯誤信息顯示的代碼。
如果我們順著上一節編寫search()視圖的思路,那么一個contact()視圖代碼應該像這樣:
from django.core.mail import send_mail
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
def contact(request):
errors = []
if request.method == 'POST':
if not request.POST.get('subject', ''):
errors.append('Enter a subject.')
if not request.POST.get('message', ''):
errors.append('Enter a message.')
if request.POST.get('email') and '@' not in request.POST['email']:
errors.append('Enter a valid e-mail address.')
if not errors:
send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get('email', 'noreply@example.com'),
['siteowner@example.com'],
)
return HttpResponseRedirect('/contact/thanks/')
return render_to_response('contact_form.html',
{'errors': errors})
(如果按照書中的示例做下來,這這里可能乎產生一個疑問:contact()視圖是否要放在books/views.py這個文件里。 但是contact()視圖與books應用沒有任何關聯,那么這個視圖應該可以放在別的地方? 這毫無緊要,只要在URLconf里正確設置URL與視圖之間的映射,Django會正確處理的。 筆者個人喜歡創建一個contact的文件夾,與books文件夾同級。這個文件夾中包括空的init.py和views.py兩個文件。
現在來分析一下以上的代碼:
確認request.method的值是’POST’。用戶瀏覽表單時這個值并不存在,當且僅當表單被提交時這個值才出現。 (在后面的例子中,request.method將會設置為’GET’,因為在普通的網頁瀏覽中,瀏覽器都使用GET,而非POST)。判斷request.method的值很好地幫助我們將表單顯示與表單處理隔離開來。
我們使用request.POST代替request.GET來獲取提交過來的數據。 這是必須的,因為contact_form.html里表單使用的是method=”post”。如果在視圖里通過POST獲取數據,那么request.GET將為空。
這里,有兩個必填項,subject 和 message,所以需要對這兩個進行驗證。 注意,我們使用request.POST.get()方法,并提供一個空的字符串作為默認值;這個方法很好的解決了鍵丟失與空數據問題。
雖然email非必填項,但如果有提交她的值則我們也需進行驗證。 我們的驗證算法相當的薄弱,僅驗證值是否包含@字符。 在實際應用中,需要更為健壯的驗證機制(Django提供這些驗證機制,稍候我們就會看到)。
我們使用了django.core.mail.send_mail函數來發送e-mail。 這個函數有四個必選參數: 主題,正文,寄信人和收件人列表。 send_mail是Django的EmailMessage類的一個方便的包裝,EmailMessage類提供了更高級的方法,比如附件,多部分郵件,以及對于郵件頭部的完整控制。
注意,若要使用send_mail()函數來發送郵件,那么服務器需要配置成能夠對外發送郵件,并且在Django中設置出站服務器地址。 參見規范:http://docs.djangoproject.com/en/dev/topics/email/
當郵件發送成功之后,我們使用HttpResponseRedirect對象將網頁重定向至一個包含成功信息的頁面。 包含成功信息的頁面這里留給讀者去編寫(很簡單 一個視圖/URL映射/一份模板即可),但是我們要解釋一下為何重定向至新的頁面,而不是在模板中直接調用render_to_response()來輸出。
原因就是: 若用戶刷新一個包含POST表單的頁面,那么請求將會重新發送造成重復。 這通常會造成非期望的結果,比如說重復的數據庫記錄;在我們的例子中,將導致發送兩封同樣的郵件。 如果用戶在POST表單之后被重定向至另外的頁面,就不會造成重復的請求了。
我們應每次都給成功的POST請求做重定向。 這就是web開發的最佳實踐。
contact()視圖可以正常工作,但是她的驗證功能有些復雜。 想象一下假如一個表單包含一打字段,我們真的將必須去編寫每個域對應的if判斷語句?
另外一個問題是表單的重新顯示。若數據驗證失敗后,返回客戶端的表單中各字段最好是填有原來提交的數據,以便用戶查看哪里出現錯誤(用戶也不需再次填寫正確的字段值)。 我們可以手動地將原來的提交數據返回給模板,并且必須編輯HTML里的各字段來填充原來的值。
# views.py
def contact(request):
errors = []
if request.method == 'POST':
if not request.POST.get('subject', ''):
errors.append('Enter a subject.')
if not request.POST.get('message', ''):
errors.append('Enter a message.')
if request.POST.get('email') and '@' not in request.POST['email']:
errors.append('Enter a valid e-mail address.')
if not errors:
send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get('email', `'noreply@example.com`_'),
[`'siteowner@example.com`_'],
)
return HttpResponseRedirect('/contact/thanks/')
return render_to_response('contact_form.html', {
'errors': errors,
'subject': request.POST.get('subject', ''),
'message': request.POST.get('message', ''),
'email': request.POST.get('email', ''),
})
# contact_form.html
<head>
<title>Contact ustitle>
head>
<h1>Contact ush1>
{% if errors %}
{% for error in errors %}
- {{ error }}</li>
{% endfor %}
l>
{% endif %}
method="post">
<p>Subject: <input type="text" name="subject" value="{{ subject }}" >p>
<p>Your e-mail (optional): <input type="text" name="email" value="{{ email }}" >p>
<p>Message: <textarea name="message" rows="10" cols="50">{{ message }}textarea>p>
<input type="submit" value="Submit">
form>
body>
html>
這看起來雜亂,且寫的時候容易出錯。 希望你開始明白使用高級庫的用意——負責處理表單及相關校驗任務。
Django帶有一個form庫,稱為django.forms,這個庫可以處理我們本章所提到的包括HTML表單顯示以及驗證。 接下來我們來深入了解一下form庫,并使用她來重寫contact表單應用。
Django的newforms庫
在Django社區上會經常看到django.newforms這個詞語。當人們討論django.newforms,其實就是我們本章里面介紹的django.forms。
改名其實有歷史原因的。 當Django一次向公眾發行時,它有一個復雜難懂的表單系統:django.forms。后來它被完全重寫了,新的版本改叫作:django.newforms,這樣人們還可以通過名稱,使用舊版本。 當Django 1.0發布時,舊版本django.forms就不再使用了,而django.newforms也終于可以名正言順的叫做:django.forms。
表單框架最主要的用法是,為每一個將要處理的HTML的`` 定義一個Form類。 在這個例子中,我們只有一個
,因此我們只需定義一個Form類。 這個類可以存在于任何地方,甚至直接寫在
views.py文件里也行,但是社區的慣例是把Form類都放到一個文件中:forms.py。在存放
views.py`` 的目錄中,創建這個文件,然后輸入:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField()
email = forms.EmailField(required=False)
message = forms.CharField()
這看上去簡單易懂,并且很像在模塊中使用的語法。 表單中的每一個字段(域)作為Form類的屬性,被展現成Field類。這里只用到CharField和EmailField類型。 每一個字段都默認是必填。要使email成為可選項,我們需要指定required=False。
讓我們鉆研到Python解釋器里面看看這個類做了些什么。 它做的第一件事是將自己顯示成HTML:
>>> from contact.forms import ContactForm
>>> f = ContactForm()
>>> print f
<th><label for="id_subject">Subject:label>th><td><input type="text" name="subject" id="id_subject" />td>tr>
<th><label for="id_email">Email:label>th><td><input type="text" name="email" id="id_email" />td>tr>
<th><label for="id_message">Message:label>th>為了便于訪問,Django用
標志,為每一個字段添加了標簽。 這個做法使默認行為盡可能合適。
默認輸出按照HTML的格式,另外有一些其它格式的輸出:
>>> print f.as_ul()
<label for="id_subject">Subject:label> "text" name="subject" id="id_subject" />li>
<label for="id_email">Email:label> "text" name="email" id="id_email" />li>
<label for="id_message">Message:label> "text" name="message" id="id_message" />li>
>>> print f.as_p()
<label for="id_subject">Subject:label> "text" name="subject" id="id_subject" />p>
<label for="id_email">Email:label> "text" name="email" id="id_email" />p>
<label for="id_message">Message:label> "text" name="message" id="id_message" />p>
請注意,標簽、、的開閉合標記沒有包含于輸出當中,這樣你就可以添加額外的行或者自定義格式。
這些類方法只是一般情況下用于快捷顯示完整表單的方法。 你同樣可以用HTML顯示個別字段:
>>> print f['subject']
type="text" name="subject" id="id_subject" />
>>> print f['message']
type="text" name="message" id="id_message" />
Form對象做的第二件事是校驗數據。 為了校驗數據,我們創建一個新的對Form象,并且傳入一個與定義匹配的字典類型數據:
>>> f = ContactForm({'subject': 'Hello', 'email': 'adrian@example.com', 'message': 'Nice site!'})
一旦你對一個Form實體賦值,你就得到了一個綁定form:
>>> f.is_bound
True
調用任何綁定form的is_valid()方法,就可以知道它的數據是否合法。 我們已經為每個字段傳入了值,因此整個Form是合法的:
>>> f.is_valid()
True
如果我們不傳入email值,它依然是合法的。因為我們指定這個字段的屬性required=False:
>>> f = ContactForm({'subject': 'Hello', 'message': 'Nice site!'})
>>> f.is_valid()
True
但是,如果留空subject或message,整個Form就不再合法了:
>>> f = ContactForm({'subject': 'Hello'})
>>> f.is_valid()
False
>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f.is_valid()
False
你可以逐一查看每個字段的出錯消息:
>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f['message'].errors
[u'This field is required.']
>>> f['subject'].errors
[]
>>> f['email'].errors
[]
←上一篇: Django 緩存機制
→下一篇:Django 集成的子框架
主站蜘蛛池模板:
中文字幕专区在线亚洲
|
日本护士视频xxxxxwww
|
性久久久久久久久
|
欧美妇色
|
欧美jizzhd精品欧美另类
|
国产日韩一区二区三区在线观看
|
日本不卡一区二区三区在线观看
|
亚洲综合日韩欧美一区二区三
|
日本aa大片在线播放免费看
|
欧美性高清另类videosex
|
亚洲黄色视屏
|
一区二区视频在线观看高清视频在线
|
国产成人精品高清不卡在线
|
www.av网站|
亚洲第一区二区快射影院
|
四虎永久免费网站入口2020
|
特级a欧美做爰片毛片
|
亚洲一区二区三区高清不卡
|
宇都宫紫苑在线
|
亚洲精品第一区二区在线
|
一二三区视频
|
免费在线视频播放
|
欧美日韩a
|
国产成人久久精品一区二区三区
|
狠狠涩|
中文字幕第6页
|
欧美一区二区三区gg高清影视
|
欧美精品区
|
欧美刺激午夜性久久久久久久
|
免费成年人视频在线观看
|
五月天免费在线视频
|
久久精品全国免费观看国产
|
嫩草影院在线观看精品视频
|
精品福利一区二区三区
|
jizzjizz大全
|
国产三级视频在线
|
国产毛片毛片精品天天看
|
欧美亚洲国产精品久久
|
国产免费福利体检区久久
|
看v片|
69做爰视频在线观看
|