Django表單驗證

weixin_34248705發表於2018-02-01

簡單的驗證
我們的搜尋示例仍然相當簡單,特別是在資料驗證方面。 我們只是檢查,以確保搜尋查詢不是空的。 許多HTML表單都包含比確保值非空的驗證級別更復雜的級別。 我們都看到了網站上的錯誤訊息:

  • “請輸入有效的電子郵件地址。 'foo'不是電子郵件地址。“
  • “請輸入有效的五位美國郵政編碼。 '123'不是郵政編碼。“
  • “請輸入YYYY-MM-DD格式的有效日期。”
  • “請輸入至少8個字元的密碼,並至少包含一個數字。”

讓我們調整我們的search()檢視,以便驗證搜尋項長度小於或等於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(request, 'search_results.html', {'books': books, 'query': q})
    return render(request, 'search_form.html', {'error': error})

現在,如果您嘗試提交長度超過20個字元的搜尋查詢,則不會讓您搜尋; 你會得到一個錯誤訊息。 但是search_form.html中的那個錯誤資訊現在說“請提交一個搜尋字詞” - 所以我們必須改變它對於這兩種情況的準確性:

<html>
<head>
    <title>Search</title>
</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(request, 'search_results.html', {'books': books, 'query': q})
    return render(request, 'search_form.html', {'errors': errors})

然後,我們需要對search_form.html模板進行一些調整,以反映它現在傳遞了錯誤列表而不是錯誤布林值:

<html>
<head>
    <title>Search</title>
</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的開發人員想到了這一點,並在Django中構建了一個更高階別的庫,用於處理與表單驗證相關的任務。

你的第一堂課

Django附帶了一個名為django.forms的表單庫,它處理我們一直在探索本章的許多問題 - 從HTML表單顯示到驗證。讓我們深入並使用Django表單框架重寫我們的聯絡表單應用程式。使用表單框架的主要方法是為每個正在處理的HTML <form>定義一個Form類。

在我們的例子中,我們只有一個<form>,所以我們將有一個Form類。 Django社群約定是將Form類儲存在一個名為forms.py的單獨檔案中。在與mysite \ views.py相同的目錄中建立該檔案,然後輸入以下內容:

# mysite_project\mysite\mysite\forms.py

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField()
    email = forms.EmailField(required=False)
    message = forms.CharField()

這非常直觀,與Django的模型語法相似。 表單中的每個欄位都由一個Field類的型別表示 - CharField和EmailField是這裡使用的唯一型別的欄位 - 作為Form類的屬性。 每個欄位預設是必需的,所以為了使電子郵件可選,我們指定required = False。

讓我們跳進Python互動式直譯器,看看這個類可以做什麼。 它能做的第一件事就是將自身顯示為HTML:

(env_mysite) C:\Users\...\mysite> python manage.py shell

>>> from mysite.forms import ContactForm
>>> f = ContactForm()
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input type="text" name="subject" required id="id_subject" /></td></tr>
<tr><th><label for="id_email">Email:</label></th><td><input type="email" name="email" id="id_email" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" required id="id_message" /></td></tr>

Django為每個欄位新增一個標籤,以及用於訪問的<label>標籤。 這個想法是使預設行為儘可能最佳。 這個預設的輸出格式是HTML <table>格式,但還有一些其他的內建輸出:

>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input type="text" name="subject" required id="id_subject" /></li>
<li><label for="id_email">Email:</label> <input type="email" name="email" id="id_email" /></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" required id="id_message" /></li>

>>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input type="text" name="subject" required id="id_subject" /></p>
<p><label for="id_email">Email:</label> <input type="email" name="email" id="id_email" /></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" required id="id_message" /></p>

請注意,開啟和關閉的<table>,<ul>和<form>標記不包含在輸出中,因此您可以根據需要新增任何額外的行和自定義。 這些方法只是顯示整個表單的常見情況的捷徑。 您還可以顯示特定欄位的HTML:

>>> print(f['subject'])
<input type="text" name="subject" required id="id_subject" />
>>> print f['message']
<input type="text" name="message" required id="id_message" />

Form物件可以做的第二件事是驗證資料。 要驗證資料,請建立一個新的Form物件並將其傳遞一個將欄位名稱對映到資料的資料字典:

>>> f = ContactForm({'subject': 'Hello', 'email': 'nige@example.com', 'message': 'Nice site!'})

一旦你關聯了一個Form例項的資料,你已經建立了一個“bound”的形式:

>>> f.is_bound
True

呼叫任何繫結窗體上的is_valid()方法,以確定其資料是否有效。 我們已經為每個欄位傳遞了一個有效的值,所以表單的整體是有效的:

>>> f.is_valid()
True

如果我們不通過電子郵件欄位,它仍然有效,因為我們已經為該欄位指定了required = False:

>>> f = ContactForm({'subject': 'Hello', 'message': 'Nice site!'})
>>> f.is_valid()
True 

但是,如果我們忽略了主題或訊息,表單不再有效:

>>> 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
['This field is required.']
>>> f['subject'].errors
[]
>>> f['email'].errors
[]

每個繫結的Form例項都有一個errors屬性,它提供了一個將欄位名稱對映到錯誤訊息列表的字典:

>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f.errors
{'message'`: ['This field is required.']}

最後,對於已經發現資料有效的表單例項,可以使用一個“ cleaned_data”屬性。 這是提交的資料字典,“清理”。 Django的表單框架不僅驗證資料, 它通過將值轉換為適當的Python型別來清除它:

>>> f = ContactForm({'subject': 'Hello', 'email': 'nige@example.com', 'message': 'Nice site!'})
>>> f.is_valid() 
True
>>> f.cleaned_data
{'subject': 'Hello', 'email': 'nige@example.com', 'message': 'Nice site!'}

我們的聯絡表單只處理被“清理”成字串物件的字串 - 但是如果我們要使用IntegerField或DateField,則表單框架將確保已清空的資料為給定的欄位使用適當的Python整數或datetime.date物件。

相關文章