Django表單介紹

HTML 表單

在HTML中,表單是<form>...</form> 之間元素的集合,它們容許訪問者輸入文本、選擇選項、操做對象和控制等等,而後將信息發送回服務器。html

某些表單的元素 —— 文本輸入和複選框 —— 很是簡單並且內建於HTML 自己。其它的表單會複雜些;例如彈出一個日期選擇對話框的界面、容許你移動滾動條的界面、使用JavaScript 和CSS 以及HTML 表單<input> 元素來實現操做控制的界面。html5

<input> 元素同樣,一個表單必須指定兩樣東西:python

  • 目的地:響應用戶輸入數據的URL
  • 方式:發送數據所使用的HTTP 方法

例如,Django Admin 站點的登陸表單包含幾個<input> 元素:type="text" 用於用戶名,type="password" 用於密碼,type="submit" 用於「Log in" 按鈕。它還包含一些用戶看不到的隱藏的文本字段,Django 使用它們來決定下一步的行爲。程序員

它還告訴瀏覽器表單數據應該發往<form> 的action 屬性指定的URL —— /admin/,並且應該使用method 屬性指定的HTTP 方法 —— post數據庫

當觸發<input type="submit" value="Log in"> 元素時,數據將發送給/admin/django

GET 和 POST

處理表單時候只會用到GET 和 POST 方法。api

Django 的登陸表單使用POST 方法,在這個方法中瀏覽器組合表單數據、對它們進行編碼以用於傳輸、將它們發送到服務器而後接收它的響應。瀏覽器

相反,GET 組合提交的數據爲一個字符串,而後使用它來生成一個URL。這個URL 將包含數據發送的地址以及數據的鍵和值。若是你在Django 文檔中作一次搜索,你會當即看到這點,此時將生成一個https://docs.djangoproject.com/search/?q=forms&release=1 形式的URL。安全

GET 和POST 用於不一樣的目的。服務器

用於改變系統狀態的請求 —— 例如,給數據庫帶來變化的請求 —— 應該使用POSTGET 只應該用於不會影響系統狀態的請求。

GET 還不適合密碼錶單,由於密碼將出如今URL 中,以及瀏覽器的歷史和服務器的日誌中,並且都是以普通的文本格式。它還不適合數據量大的表單和二進制數據,例如一張圖片。使用GET 請求做爲管理站點的表單具備安全隱患:攻擊者很容易模擬表單請求來取得系統的敏感數據。POST,若是與其它的保護措施結合將對訪問提供更多的控制,例如Django 的CSRF 保護

另外一個方面,GET 適合網頁搜索這樣的表單,由於這種表示一個GET 請求的URL 能夠很容易地做爲書籤、分享和從新提交。

Django 在表單中的角色

處理表單是一件很複雜的事情。考慮一下Django 的Admin 站點,不一樣類型的大量數據項須要在一個表單中準備好、渲染成HTML、使用一個方便的界面編輯、返回給服務器、驗證並清除,而後保存或者向後繼續處理。

Django 的表單功能能夠簡化並自動化大部分這些工做,並且還能夠比大部分程序員本身所編寫的代碼更安全。

Django 會處理表單工做中的三個顯著不一樣的部分:

  • 準備數據、重構數據,以便下一步提交。
  • 爲數據建立HTML 表單
  • 接收並處理客戶端提交的表單和數據

能夠手工編寫代碼來實現,可是Django 能夠幫你完成全部這些工做。

Django 中的表單

咱們已經簡短講述HTML 表單,可是HTML的<form> 只是其機制的一部分。

在一個Web 應用中,"表單"可能指HTML <form>、或者生成它的Django 的Form、或者提交時發送的結構化數據、或者這些部分的總和。

Django 的Form 類

表單系統的核心部分是Django 的Form 類。Django 的模型描述一個對象的邏輯結構、行爲以及展示給咱們的方式,與此相似,Form 類描述一個表單並決定它如何工做和展示。

就像模型類的屬性映射到數據庫的字段同樣,表單類的字段會映射到HTML 的<input>表單的元素。ModelForm 經過一個Form 映射模型類的字段到HTML 表單的<input> 元素;Django 的Admin 站點就是基於這個)。

表單的字段自己也是類;它們管理表單的數據並在表單提交時進行驗證。DateField 和FileField 處理的數據類型差異很大,必須完成不一樣的事情。

表單字段在瀏覽器中呈現給用戶的是一個HTML 的「widget」  —— 用戶界面的一個片斷。每一個字段類型都有一個合適的默認Widget 類,須要時能夠覆蓋。

實例化、處理和渲染表單

在Django 中渲染一個對象時,咱們一般:

  1. 在視圖中得到它(例如,從數據庫中獲取)
  2. 將它傳遞給模板上下文
  3. 使用模板變量將它擴展爲HTML 標記

在模板中渲染表單和渲染其它類型的對象幾乎同樣,除了幾個關鍵的差異。

在模型實例不包含數據的狀況下,在模板中對它作處理不多有什麼用處。可是渲染一個未填充的表單卻很是有意義 —— 咱們但願用戶去填充它。

因此當咱們在視圖中處理模型實例時,咱們通常從數據庫中獲取它。當咱們處理表單時,咱們通常在視圖中實例化它。

當咱們實例化表單時,咱們能夠選擇讓它爲空仍是預先填充它,例如使用:

  • 來自一個保存後的模型實例的數據(例如用於編輯的管理表單)
  • 咱們從其它地方得到的數據
  • 從前面一個HTML 表單提交過來的數據

最後一種狀況最使人關注,由於它使得用戶能夠不僅是閱讀一個網站,並且能夠給網站返回信息。

構建一個表單

須要完成的工做

假設你想在你的網站上建立一個簡單的表單,以得到用戶的名字。你須要相似這樣的模板:

<form action="/your-name/" method="post"> <label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" value="{{ current_name }}"> <input type="submit" value="OK"> </form> 

這告訴瀏覽器發送表單的數據到URL /your-name/,並使用POST 方法。它將顯示一個標籤爲"Your name:"的文本字段,和一個"OK"按鈕。若是模板上下文包含一個current_name 變量,它將用於預填充your_name 字段。

你將須要一個視圖來渲染這個包含HTML 表單的模板,並提供合適的current_name 字段。

當表單提交時,發往服務器的POST 請求將包含表單數據。

如今你還須要一個對應/your-name/ URL 的視圖,它在請求中找到正確的鍵/值對,而後處理它們。

這是一個很是簡單的表單。實際應用中,一個表單可能包含幾十上百個字段,其中大部分須要預填充,並且咱們預料到用戶未來回編輯-提交幾回才能完成操做。

咱們可能須要在表單提交以前,在瀏覽器端做一些驗證。咱們可能想使用很是複雜的字段,以容許用戶作相似從日曆中挑選日期這樣的事情,等等。

這個時候,讓Django 來爲咱們完成大部分工做是很容易的。

在Django 中構建一個表單

Form 類

咱們已經計劃好了咱們的 HTML 表單應該呈現的樣子。在Django 中,咱們的起始點是這裏:

forms.py
from django import forms class NameForm(forms.Form): your_name = forms.CharField(label='Your name', max_length=100) 

它定義一個Form 類,只帶有一個字段(your_name)。咱們已經對這個字段使用一個友好的標籤,當渲染時它將出如今<label> 中(在這個例子中,即便咱們省略它,咱們指定的label仍是會自動生成)。

字段容許的最大長度經過max_length 定義。它完成兩件事情。首先,它在HTML 的<input> 上放置一個maxlength="100" (這樣瀏覽器將在第一時間阻止用戶輸入多於這個數目的字符)。它還意味着當Django 收到瀏覽器發送過來的表單時,它將驗證數據的長度。

Form 的實例具備一個is_valid() 方法,它爲全部的字段運行驗證的程序。當調用這個方法時,若是全部的字段都包含合法的數據,它將:

  • 返回True
  • 將表單的數據放到cleaned_data 屬性中。

完整的表單,第一次渲染時,看上去將像:

<label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" maxlength="100"> 

注意它不包含 <form> 標籤和提交按鈕。咱們必須本身在模板中提供它們。

視圖

發送給Django 網站的表單數據經過一個視圖處理,通常和發佈這個表單的是同一個視圖。這容許咱們重用一些相同的邏輯。

要操做一個經過URL發佈的表單,咱們要在視圖中實例表單。

views.py
from django.shortcuts import render from django.http import HttpResponseRedirect from .forms import NameForm def get_name(request): # if this is a POST request we need to process the form data if request.method == 'POST': # create a form instance and populate it with data from the request: form = NameForm(request.POST) # check whether it's valid: if form.is_valid(): # process the data in form.cleaned_data as required # ... # redirect to a new URL: return HttpResponseRedirect('/thanks/') # if a GET (or any other method) we'll create a blank form else: form = NameForm() return render(request, 'name.html', {'form': form}) 

若是訪問視圖的是一個GET 請求,它將建立一個空的表單實例並將它放置到要渲染的模板的上下文中。這是咱們在第一次訪問該URL 時預期發生的狀況。

若是表單的提交使用POST 請求,那麼視圖將再次建立一個表單實例並使用請求中的數據填充它:form =NameForm(request.POST)這叫作」綁定數據至表單「(它如今是一個綁定的表單)。

咱們調用表單的is_valid() 方法;若是它不爲True,咱們將帶着這個表單返回到模板。這時表單再也不爲空(未綁定),因此HTML 表單將用以前提交的數據填充,而後能夠根據要求編輯並改正它。

若是is_valid() 爲True,咱們將可以在cleaned_data 屬性中找到全部合法的表單數據。在發送HTTP 重定向給瀏覽器告訴它下一步的去向以前,咱們能夠用這個數據來更新數據庫或者作其它處理。

模板

咱們不須要在name.html 模板中作不少工做。最簡單的例子是:

<form action="/your-name/" method="post"> {% csrf_token %} {{ form }} <input type="submit" value="Submit" /> </form> 

根據{{ form }},全部的表單字段和它們的屬性將經過Django 的模板語言拆分紅HTML 標記 。

表單和跨站請求僞造的防禦

Django 原生支持一個簡單易用的跨站請求僞造的防禦當提交一個啓用CSRF 防禦的POST 表單時,你必須使用上面例子中的csrf_token 模板標籤。然而,由於CSRF 防禦在模板中不是與表單直接捆綁在一塊兒的,這個標籤在這篇文檔的如下示例中將省略。

HTML5 輸入類型和瀏覽器驗證

若是你的表單包含URLFieldEmailField 或其它整數字段類型,Django 將使用urlemail和 number 這樣的HTML5 輸入類型。默認狀況下,瀏覽器可能會對這些字段進行它們自身的驗證,這些驗證可能比Django 的驗證更嚴格。若是你想禁用這個行爲,請設置form 標籤的novalidate 屬性,或者指定一個不一樣的字段,如TextInput

如今咱們有了一個能夠工做的網頁表單,它經過Django Form 描述、經過視圖處理並渲染成一個HTML <form>

這是你入門所須要知道的全部內容,可是表單框架爲了便利提供了更多的內容。一旦你理解了上面描述的基本處理過程,你應該能夠理解表單系統的其它功能並準備好學習更多的底層機制。

Django Form 類詳解

全部的表單類都做爲django.forms.Form 的子類建立,包括你在Django 管理站點中遇到的ModelForm

模型和表單

實際上,若是你的表單打算直接用來添加和編輯Django 的模型,ModelForm 能夠節省你的許多時間、精力和代碼,由於它將根據Model 類構建一個表單以及適當的字段和屬性。 

綁定的和未綁定的表單實例

綁定的和未綁定的表單 之間的區別很是重要:

  • 未綁定的表單沒有關聯的數據。當渲染給用戶時,它將爲空或包含默認的值。
  • 綁定的表單具備提交的數據,所以能夠用來檢驗數據是否合法。若是渲染一個不合法的綁定的表單,它將包含內聯的錯誤信息,告訴用戶如何糾正數據。

表單的is_bound 屬性將告訴你一個表單是否具備綁定的數據。

字段詳解

考慮一個比上面的迷你示例更有用的一個表單,咱們能夠用它來在一個我的網站上實現「contact me」功能:

forms.py
from django import forms class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField(widget=forms.Textarea) sender = forms.EmailField() cc_myself = forms.BooleanField(required=False) 

咱們前面的表單只使用一個字段your_name,它是一個CharField在這個例子中,咱們的表單具備四個字段:subjectmessagesender 和cc_myself共用到三種字段類型:CharFieldEmailField 和 BooleanField完整的字段類型列表能夠在表單字段中找到。

窗口小部件

每一個表單字段都有一個對應的Widget 類,它對應一個HTML 表單Widget,例如<input type="text">

在大部分狀況下,字段都具備一個合理的默認Widget。例如,默認狀況下,CharField 具備一個TextInput Widget,它在HTML 中生成一個<input type="text">若是你須要<textarea>,在定義表單字段時你應該指定一個合適的Widget,例如咱們定義的message字段。

字段的數據

無論表單提交的是什麼數據,一旦經過調用is_valid() 成功驗證(is_valid() 返回True),驗證後的表單數據將位於form.cleaned_data 字典中。這些數據已經爲你轉換好爲Python 的類型。

此時,你依然能夠從request.POST 中直接訪問到未驗證的數據,可是訪問驗證後的數據更好一些。

在上面的聯繫表單示例中,cc_myself 將是一個布爾值。相似地,IntegerField 和FloatField 字段分別將值轉換爲Python 的intfloat

下面是在視圖中如何處理表單數據:

views.py
from django.core.mail import send_mail if form.is_valid(): subject = form.cleaned_data['subject'] message = form.cleaned_data['message'] sender = form.cleaned_data['sender'] cc_myself = form.cleaned_data['cc_myself'] recipients = ['info@example.com'] if cc_myself: recipients.append(sender) send_mail(subject, message, sender, recipients) return HttpResponseRedirect('/thanks/') 

提示

關於Django 中如何發送郵件的更多信息,請參見發送郵件

有些字段類型須要一些額外的處理。例如,使用表單上傳的文件須要不一樣地處理(它們能夠從request.FILES 獲取,而不是request.POST)。如何使用表單處理文件上傳的更多細節,請參見綁定上傳的文件到一個表單

使用表單模板

你須要作的就是將表單實例放進模板的上下文。若是你的表單在Context 中叫作form,那麼 {{ form }} 將正確地渲染它的<label> 和<input>元素。

表單渲染的選項

表單模板的額外標籤

不要忘記,表單的輸出不 包含<form> 標籤,和表單的submit 按鈕。你必須本身提供它們。

對於<label>/<input> 對,還有幾個輸出選項:

  • {{ form.as_table }} 以表格的形式將它們渲染在<tr> 標籤中
  • {{ form.as_p }}  將它們渲染在<p> 標籤中
  • {{ form.as_ul }} 將它們渲染在<li> 標籤中

注意,你必須本身提供<table> 或<ul> 元素。

下面是咱們的ContactForm 實例的輸出{{ form.as_p }}

<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p> <p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p> <p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></p> <p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p> 

注意,每一個表單字段具備一個ID 屬性並設置爲id_<field-name>,它被一塊兒的label 標籤引用。它對於確保屏幕閱讀軟件這類的輔助計算很是重要。你還能夠自定義label 和 id 生成的方式

更多信息參見 輸出表單爲HTML

手工渲染字段

咱們沒有必要非要讓Django 來分拆表單的字段;若是咱們喜歡,咱們能夠手工來作(例如,這樣容許從新對字段排序)。每一個字段都是表單的一個屬性,可使用{{ form.name_of_field }} 訪問,並將在Django 模板中正確地渲染。例如:

{{ form.non_field_errors }} <div class="fieldWrapper"> {{ form.subject.errors }} <label for="{{ form.subject.id_for_label }}">Email subject:</label> {{ form.subject }} </div> <div class="fieldWrapper"> {{ form.message.errors }} <label for="{{ form.message.id_for_label }}">Your message:</label> {{ form.message }} </div> <div class="fieldWrapper"> {{ form.sender.errors }} <label for="{{ form.sender.id_for_label }}">Your email address:</label> {{ form.sender }} </div> <div class="fieldWrapper"> {{ form.cc_myself.errors }} <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label> {{ form.cc_myself }} </div> 

完整的<label> 元素還可使用label_tag() 生成。例如:

<div class="fieldWrapper"> {{ form.subject.errors }} {{ form.subject.label_tag }} {{ form.subject }} </div> 

渲染表單的錯誤信息

固然,這個便利性的代價是更多的工做。直到如今,咱們沒有擔憂如何展現錯誤信息,由於Django 已經幫咱們處理好。在下面的例子中,咱們將本身處理每一個字段的錯誤和表單總體的各類錯誤。注意,表單和模板頂部的{{ form.non_field_errors }} 查找每一個字段的錯誤。

使用{{ form.name_of_field.errors }} 顯示錶單錯誤的一個清單,並渲染成一個ul。看上去可能像:

<ul class="errorlist"> <li>Sender is required.</li> </ul> 

這個ul 有一個errorlist CSS 類型,你能夠用它來定義外觀。若是你但願進一步自定義錯誤信息的顯示,你能夠迭代它們來實現:

{% if form.subject.errors %} <ol> {% for error in form.subject.errors %} <li><strong>{{ error|escape }}</strong></li> {% endfor %} </ol> {% endif %} 

非字段錯誤(以及使用form.as_p() 時渲染的隱藏字段錯誤)將渲染成一個額外的CSS 類型nonfield 以助於和字段錯誤信息區分。例如,{{ form.non_field_errors }} 看上去會像:

<ul class="errorlist nonfield"> <li>Generic validation error</li> </ul> 
Changed in Django 1.8:

添加上面示例中提到的nonfield CSS 類型。

參見Forms API 以得到關於錯誤、樣式以及在模板中使用表單屬性的更多內容。

迭表明單的字段

若是你爲你的表單使用相同的HTML,你可使用{% for %} 循環迭代每一個字段來減小重複的代碼:

{% for field in form %} <div class="fieldWrapper"> {{ field.errors }} {{ field.label_tag }} {{ field }} </div> {% endfor %} 

{{ field }} 中有用的屬性包括:

{{ field.label }}
字段的label,例如Email address
{{ field.label_tag }}

包含在HTML <label> 標籤中的字段Label。它包含表單的label_suffix例如,默認的label_suffix 是一個冒號:

<label for="id_email">Email address:</label>
{{ field.id_for_label }}
用於這個字段的ID(在上面的例子中是id_email)。 若是你正在手工構造label,你可能想使用它代替label_tag 若是你有一些內嵌的JavaScript 而且想避免硬編碼字段的ID,這也是有用的。
{{ field.value }}
字段的值,例如someone@example.com
{{ field.html_name }}
輸入元素的name 屬性中將使用的名稱。 它將考慮到表單的前綴。
{{ field.help_text }}
與該字段關聯的幫助文檔。
{{ field.errors }}
輸出一個<ul class="errorlist">,包含這個字段的驗證錯誤信息。 你可使用{% for error in field.errors %}自定義錯誤的顯示。  這種狀況下,循環中的每一個對象只是一個包含錯誤信息的簡單字符串。
{{ field.is_hidden }}
若是字段是隱藏字段,則爲True,不然爲False 做爲模板變量,它不是頗有用處,可是能夠用於條件測試,例如:
{% if field.is_hidden %}  {% endif %} 
{{ field.field }}
表單類中的Field 實例,經過BoundField 封裝。 你可使用它來訪問Field 屬性,例如{% char_field.field.max_length %}

迭代隱藏和可見的字段

若是你正在手工佈局模板中的一個表單,而不是依賴Django 默認的表單佈局,你可能但願將<input type="hidden"> 字段與非隱藏的字段區別對待。例如,由於隱藏的字段不會顯示,在該字段旁邊放置錯誤信息可能讓你的用戶感到困惑 —— 因此這些字段的錯誤應該有區別地來處理。

Django 提供兩個表單方法,它們容許你獨立地在隱藏的和可見的字段上迭代:hidden_fields() 和visible_fields()下面是使用這兩個方法對前面一個例子的修改:


{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}  {% for field in form.visible_fields %} <div class="fieldWrapper"> {{ field.errors }} {{ field.label_tag }} {{ field }} </div> {% endfor %} 

這個示例沒有處理隱藏字段中的任何錯誤信息。一般,隱藏字段中的錯誤意味着表單被篡改,由於正常的表單填寫不會改變它們。然而,你也能夠很容易地爲這些表單錯誤插入一些錯誤信息顯示出來。

可重用的表單模板

若是你的網站在多個地方對錶單使用相同的渲染邏輯,你能夠保存表單的循環到一個單獨的模板中來減小重複,而後在其它模板中使用include 標籤來重用它:

# In your form template:
{% include "form_snippet.html" %} # In form_snippet.html: {% for field in form %} <div class="fieldWrapper"> {{ field.errors }} {{ field.label_tag }} {{ field }} </div> {% endfor %} 

若是傳遞到模板上下文中的表單對象具備一個不一樣的名稱,你可使用include 標籤的with 參數來對它起個別名:

{% include "form_snippet.html" with form=comment_form %} 

若是你發現本身常常這樣作,你可能須要考慮一下建立一個自定義的 inclusion 標籤

相關文章
相關標籤/搜索