Forms
HTML form是交互網頁的支柱。下面來學習一下有關使用Django處理用戶提交的表單數據,驗證等功能。
我們將討論HttpRequest和Form對象。
- request中包含的信息
在views.py中的每一個用於顯示頁面的函數都需要以request作為第一個函數參數。request包含了一些有用
的信息,如:
request.path 除去了域名和端口的訪問路徑,
request.get_host 域名+端口信息
request.get_full_path() 所有路徑,包含傳遞的參數
requets.is_secure() 是否使用https進行鏈接


還有一個特別的屬性request.META,它是一個dict類型,包含了所有HTTP header信息,不過也要根據用戶
的實際環境來決定。如
HTTP_REFERER
HTTP_USER_AGENT 用戶使用的瀏覽器信息
REMOTE_ADDR 用戶的IP,如果使用代理,也可以顯示出來
(有很多屬性,只是截圖了一些)

並不是每一個用戶提交的頭信息都相同,所以在讀取的時候,要考慮一些異常情況,比如取不到相應的值的情況。
可以使用try...except,也可以使用dict.get(key, default)中默認返回為預設值的方法。
def ua_display_good1(request):
try:
ua = request.META['HTTP_USER_AGENT']
except KeyError:
ua = 'unknown'
return HttpResponse("Your browser is %s" % ua)
def ua_display_good2(request):
ua = request.META.get('HTTP_USER_AGENT', 'unknown')
return HttpResponse("Your browser is %s" % ua)
除了以上的這些元數據的信息,HttpRequest對象還包括兩個屬性包括了用戶提交的數據:
request.GET和request.POST。它們都是dictionary-like對象。意思就是說可以像dict那樣
操作,但不是dict,還有一些dict沒有的操作。
下面構造一個用於用戶搜索的簡單form例子。
#template/search_form.html
<
form
action=
"/search/"
method=
"get">
< input type= "text" name= "q">
< input type= "submit" value= "Search">
</ form>
< input type= "text" name= "q">
< input type= "submit" value= "Search">
</ form>
#template/search_results.html
<
p>Your searched for '{{ query }}'
{% if books %}
{% for book in books %}
< li>{{ book. title }}</ li>
{% endfor %}
{% else %}
< p>No books found.
{% endif %}
{% if books %}
{% for book in books %}
< li>{{ book. title }}</ li>
{% endfor %}
{% else %}
< p>No books found.
{% endif %}
# urls.py
from django.conf.urls import patterns, include, url
from books.views import hello, search_form, search
urlpatterns = patterns( '',
url(r '^search-form/$', search_form),
url(r '^search/$', search),
)
#views.py
from django.http import HttpResponse
from django.shortcuts import render_to_response
from books.models import Book
def search_form(request) :
return render_to_response( 'search_form.html')
def search(request) :
if 'q' in request.GET : #GET是一個dict,使用文本框的name作為key
#在這里需要做一個判斷,是否存在提交數據,以免報錯
q = request.GET[ 'q']
#使用lookup后綴,意思為書名不區分大小寫,包含q就可以
books = Book.objects. filter(title__icontains =q)
return render_to_response( 'search_results.html', { 'books' : books, 'query' :q})
else :
message = 'You submitted an empty form.'
#只是簡單的返回一個response對象,因為沒有使用模塊,所以也不用渲染數據Context
return HttpResponse(message)
from django.conf.urls import patterns, include, url
from books.views import hello, search_form, search
urlpatterns = patterns( '',
url(r '^search-form/$', search_form),
url(r '^search/$', search),
)
#views.py
from django.http import HttpResponse
from django.shortcuts import render_to_response
from books.models import Book
def search_form(request) :
return render_to_response( 'search_form.html')
def search(request) :
if 'q' in request.GET : #GET是一個dict,使用文本框的name作為key
#在這里需要做一個判斷,是否存在提交數據,以免報錯
q = request.GET[ 'q']
#使用lookup后綴,意思為書名不區分大小寫,包含q就可以
books = Book.objects. filter(title__icontains =q)
return render_to_response( 'search_results.html', { 'books' : books, 'query' :q})
else :
message = 'You submitted an empty form.'
#只是簡單的返回一個response對象,因為沒有使用模塊,所以也不用渲染數據Context
return HttpResponse(message)

還有一點,form中的action屬性如果為空的,代表提交數據到當前頁面。
關於表單驗證
可以在server端進行,有時會加重server負擔,也可以使用Javascript在client端進行。有一點需要注意的是,
即使你使用了javascript在client進行了驗證,還是需要在server端對用戶提交的數據進行審查,比如有些人
會在瀏覽器中禁用javascript,也有些人會來搞注入攻擊。總之,不能相信用戶提交的提交,進行防御性編碼。
關於使用重定向
你應當總是在一個成功處理POST請求之后進行redirect,這樣可以避免用戶多次點擊提交按鈕帶來的二次請求
問題,有時會造成不好的后果,如向數據庫插入重復記錄等。
from django.http import HttpResponseRedirect
return HttpResponseRedirect('/success/')
- 使用Django的form system來簡化表單工作
在app文件夾下創建forms.py,其實在哪里創建都可以:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField()
email = forms.EmailField(required=False)
message = forms.CharField()
form的定義和model類的定義很像。我們可以使用python manage.py shell來查看form類的信息。

可以看到打印出來的f的信息其實是網頁格式的。
也可以改變默認表格的格式。

這里的<table>,<ul>都沒有包含在輸出里,所以可以自由地添加行等。
除了打印出整個form的HTML內容,還可以打印出局的字段的內容。

還可以使用form類進行驗證,使用dict對form類進行初始化。
>>> f = ContactForm({'subject': 'Hello', 'email': 'adrian@example.com', 'message': 'Nice'})
>>> f.is_bound #是否綁定表單類
True
>>> f.is_valid() #數據內容是否合法
True
當數據全部合法時,可以使用clean_data這個屬性,用來得到經過'clean'格式化的數據,會所提交過
來的數據轉化成合適的Python的類型。

如何在view中使用form類
# views.py
from django.shortcuts import render_to_response
from mysite.contact.forms import ContactForm
def contact(request) :
from django.shortcuts import render_to_response
from mysite.contact.forms import ContactForm
def contact(request) :
#可以不寫,因為python中if中定義的變量,也可以在整個函數中可見
form
=
None
if request.method == 'POST' :
form = ContactForm(request.POST)
if form.is_valid() :
cd = form.cleaned_data
send_mail(
cd[ 'subject'],
cd[ 'message'],
cd.get( 'email', 'noreply@example.com'),
[ 'siteowner@example.com'],
)
return HttpResponseRedirect( '/contact/thanks/') #需要在urls.py中配置路徑
else :
form = ContactForm()
return render_to_response( 'contact_form.html', { 'form' : form})
if request.method == 'POST' :
form = ContactForm(request.POST)
if form.is_valid() :
cd = form.cleaned_data
send_mail(
cd[ 'subject'],
cd[ 'message'],
cd.get( 'email', 'noreply@example.com'),
[ 'siteowner@example.com'],
)
return HttpResponseRedirect( '/contact/thanks/') #需要在urls.py中配置路徑
else :
form = ContactForm()
return render_to_response( 'contact_form.html', { 'form' : form})
# contact_form.html
<
html>
< head>
< title>Contact us</ title>
</ head>
< body>
< h1>Contact us</ h1>
{% if form.errors %}
< p style= "color: red;">
< head>
< title>Contact us</ title>
</ head>
< body>
< h1>Contact us</ h1>
{% if form.errors %}
< p style= "color: red;">
{# pulralize 是filter,用來判斷是否為添加's'來表示單詞的復數形式#}
Please correct the error{{ form.errors|pluralize }} below.
</ p>
{% endif %}
< form action= "" method= "post">
< table>
{# 可以使用as_ul, as_p來改變格式 #}
{{ form.as_table }}
</ table>
< input type= "submit" value= "Submit">
</ form>
</ body>
</ html>
Please correct the error{{ form.errors|pluralize }} below.
</ p>
{% endif %}
< form action= "" method= "post">
< table>
{# 可以使用as_ul, as_p來改變格式 #}
{{ form.as_table }}
</ table>
< input type= "submit" value= "Submit">
</ form>
</ body>
</ html>

在構造form的時候,還可以改變字段的構造參數
class ContactForm(forms.Form)
:
subject = forms.CharField(max_length = 100)
email = forms.EmailField(required = False)
message = forms.CharField(widget =forms.Textarea)
subject = forms.CharField(max_length = 100)
email = forms.EmailField(required = False)
message = forms.CharField(widget =forms.Textarea)
還可以給form類添加默認值
form = ContactForm(
initial={'subject': 'I love your site!'}
)
給form類自定義驗證規則,如果想要重用驗證機制,可以單獨創建新的字段類,重新寫它的驗證方法。
一般的可以直接在form類加入clean_字段名的方法,Django會自動查找以clean_開頭的函數名,並會在
驗證該字段的時候,運行這個函數。
from django
import forms
class ContactForm(forms.Form) :
subject = forms.CharField(max_length = 100)
email = forms.EmailField(required = False)
message = forms.CharField(widget =forms.Textarea)
def clean_message( self) :
message = self.cleaned_data[ 'message']
num_words = len(message.split())
if num_words < 4 :
raise forms.ValidationError( "Not enough words!")
return message
class ContactForm(forms.Form) :
subject = forms.CharField(max_length = 100)
email = forms.EmailField(required = False)
message = forms.CharField(widget =forms.Textarea)
def clean_message( self) :
message = self.cleaned_data[ 'message']
num_words = len(message.split())
if num_words < 4 :
raise forms.ValidationError( "Not enough words!")
return message
在自定義的驗證函數中,我們必須顯示的返回字段名的內容,否則會帶來表單數據丟失。
可以改變字段的label顯示的名稱
email = forms.EmailField(required=False, label='Your e-mail address')