1. 程式人生 > >Django表單之使用表單

Django表單之使用表單

本文轉載自http://www.liujiangblog.com/course/django/152,供記錄學習使用。
假設你想從表單接收使用者名稱資料,一般情況下,你需要在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>

<form action='/your-name/" method = "post">這一行定義了我們的傳送目的地/your-name/和HTTP方法POST。form元素內部還定義了一個說明標籤<label>和一個傳送按鈕submit,以及最關鍵的接收使用者輸入的<input>元素。

一、 編寫表單類

我們可以通過Django提供的Form類來生成上面的表單,不在需要手動在HTML中編寫。
首先,在你當前app內建一個form.py檔案,然後輸入下面的內容:

from django import forms

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

要點:

  • 提前匯入forms模組
  • 所有的表單類都要繼承forms.Form類
  • 每個表單欄位都有自己的欄位型別,比如CharField,它們分別對應一種HTML語言中的<form>元素中的表單元素。
  • 例子中的label用於設定說明標籤
  • max_length限制最大長度為100,。它同時起到兩個作用,一是在瀏覽器頁面限制使用者輸入不可超過100個字元,二是在後端伺服器驗證使用者輸入的長度不可超過100.
(警告:由於瀏覽器頁面可以被篡改、偽造、禁用、跳過的,所有的HTML手段的資料驗證只能防止意外不能防止惡意行為,是沒有安全保證的,破壞分子完全可以跳過瀏覽器的防禦手段偽造傳送請求!所以,在服務後端,必須將前端當做‘裸機’來對待,再次進完全徹底的資料驗證和安全防護!)

每個Django表單的例項都有一個內建的is_valid()方法,來驗證接收資料是否合法。如果所有資料都合法,那麼該方法將返回True,並將所有的表單資料轉存到它的一個叫做cleaned_data的屬性中,該屬性是一個字典型別的資料。

當我們將上面的表單渲染成真正的HTML元素,其內容如下:

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

一定要注意,它不包含<form>標籤本身以及提交按鈕!!!,為什麼要這樣呢?方便你自己控制表單動作和css,js以及其它類似bootstrap框架的嵌入

二、檢視處理

需要在檢視中,例項化我們編寫好的表單類

# views.py

from django.shortcuts import render
from django.http import HttpResponseRedirect

from .forms import NameForm

def get_name(request):
    # 如果form通過POST方法傳送資料
    if request.method == 'POST':
        # 接受request.POST引數構造form類的例項
        form = NameForm(request.POST)
        # 驗證資料是否合法
        if form.is_valid():
            # 處理form.cleaned_data中的資料
            # ...
            # 重定向到一個新的URL
            return HttpResponseRedirect('/thanks/')

    # 如果是通過GET方法請求資料,返回一個空的表單
    else:
        form = NameForm()

    return render(request, 'name.html', {'form': form})

要點是:

  • 對於GET方法請求頁面時,返回空的表單,讓使用者可以填寫資料;
  • 對於POST方法,接收表單資料,並驗證;
  • 如果資料合法,按照正常的業務邏輯繼續執行下去;
  • 如果不合法,返回一個包含先前資料的表單給前端,方便使用者修改。
    通過表單is_bound屬性可以獲知一個表單已經綁定了資料,還是一個空表。

三、模板處理

在Django的模板中,我們只需要按下面處理,就可以得到完整的HTML頁面:

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

要點:

  • <form>...</form>標籤要自己寫
  • 使用POST的方法時必須新增{% csrf_token %}標籤,用於處理csrf安全機制
  • {{ form }}代表Django為你生成其他所有的form標籤元素,也就是我們上面做的事情;
  • 提交按鈕需要手動新增
    提示:預設情況下,Django支援HTML5的表單驗證功能,比如郵箱驗證、必填專案驗證等等。

四、高階技巧

上面的例子中,只有一個使用者名稱輸入框,太簡單了,實際上有更多的表單元素。看下面的例子:

from django import forms

class ContactForm(forms.Form):
	subject = forms.CharField(max_length=100)
	message = forms.CharField(widget=forms.Texarea)
	sender = forms.EmailField()
	cc_myself = forms.BooleanField(required=False)

這個例子就有四個框組了。實際上Django的表單模組為我們內建了許多表單欄位,如下所示:

  • BooleanField
  • CharField
  • ChoiceField
  • TypedChoiceField
  • DateField
  • DateTimeField
  • DecimalField
  • DurationField
  • EmailField
  • FileField
  • FilePathField
  • FloatField
  • ImageField
  • IntegerField
  • GenericlPAddressField
  • MultipleChoiceField
  • TypedMultipleChoiceField
  • NullBooleanField
  • RegexField
  • SlugField
  • TimeField
  • URLField
  • UUIDField
  • ComboField
  • MultiValueField
  • SplitDateTimeField
  • ModelChoiceField
  • ModelMultipleChoiceField

每一個表單欄位型別都對應一種Widget類,每一種Widget類都對應了HTML語言中的一種input元素型別,比如<input type="text">。需要在HTML中實際使用什麼型別的input,就需要在Django的表單欄位中選擇相應的field。比如要一個<input type="text">,可以選擇一個CharField.

一旦你的表單接收資料並驗證通過了,那麼就可以從form.cleaned_data字典中讀取所有的表單資料,下面是一個例子:

#view.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 = ['[email protected]']
    if cc_myself:
    	recipients.append(sender)
    send_mail(subject,message,sender,recipients)
    return HttpResponseRedirect('/thanks/')

五、使用表單模板

1.表單渲染格式

前面我們通過{{ form }}模板語言,簡單地將表單渲染到HTML頁面中了,實際上,有更多的方式:

  • {{ form.as_table }}將表單渲染成一個表格元素,每一個輸入框為一個<tr>標籤
  • {{ form.as_p}}將表單的每一個輸入框包裹在一個<p>標籤內tags
  • {{ form.as_ul }} 將表單渲染成一個列表元素,每個輸入框作為一個<li>標籤
    注意:你要自己手動編寫<table><url>標籤。
    下面是將上面的ContactForm作為{{ form.as_p }}
<p><label for="id_subject">Subject:</label>
    <input id="id_subject" type="text" name="subject" maxlength="100" required /></p>
<p><label for="id_message">Message:</label>
    <textarea name="message" id="id_message" required></textarea></p>
<p><label for="id_sender">Sender:</label>
    <input type="email" name="sender" id="id_sender" required /></p>
<p><label for="id_cc_myself">Cc myself:</label>
    <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>

注意:Django自動為每個input元素設定了一個id名稱,對應label的for引數

2.手動渲染表單欄位

直接{{ form }}雖然好,啥都不用操心,但往往並不是你想要的,比如你要使用CSS和JS,比如你要引入Bootstraps框架,這些都需要對錶單內的input元素進行額外控制,需要手段渲染。

可以通過{{ form.name_of_field }}獲取每一個欄位,然後分別渲染,如下例所示:

{{ 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>

這樣是不是更靈活了呢?但是靈活的代價就是我們需要寫更多的程式碼,又偏向於原生的HTML程式碼多了一點。

3.渲染表單錯誤資訊

注意上面的例子中,我們使用{{ form.name_of_field.errors }}模板語法,在表單裡處理錯誤資訊。對於每一個表單欄位的錯誤,它其實會實際生成一個無序列表,參考下面的樣子:

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

這個列表有一個預設的css樣式類errorlist,如果你想進一步定製這個樣式,可以迴圈錯誤列表裡面的內容,然後單獨設定樣式:

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

一切非欄位的錯誤資訊,比如表單錯誤,隱藏欄位的錯誤都儲存在{{ form.non_field_errors }}中,上面的例子,我們把它放在了表單的外圍上面,它將被按照下面的HTML和CSS格式渲染:

<ul class="errorlist nonfield">
    <li>Generic validation error</li>
</ul>

4.迴圈表單的欄位:

如果你的表單欄位有相同的格式的HTML表現,那麼可以完全迴圈生成,不必要手動編寫每個欄位,減少冗餘的重複程式碼,只需要使用模板語言中的{% for %}迴圈,如下所示:

{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
        {% if field.help_text %}
        <p class="help">{{ field.help_text|safe }}</p>
        {% endif %}
    </div>
{% endfor %}

下面是{{ field }}中非常有用的屬性,這些都是Django內建的模板語言給我們提供的方便:

屬性 說明
{{ field.label}} 欄位對應的label屬性
{{ field.label_tag }} 自動生成欄位的label標籤,注意與{{ field.label }}的區別
{{ field.id_for_label }} 自定義欄位標籤的id
{{ field.value}} 當前欄位的值,比如一個Email欄位的值[email protected]
{{ field.html_name }} 指定欄位生成的input標籤中name屬性的值
{{ field.help_text }} 欄位的幫助屬性
{{ field.errors }} 包含錯誤資訊的元素
{{ field.is_hidden }} 用於判斷當前欄位是否為隱藏的欄位,如果是,返回True
{{ field.field }} 返回欄位的引數列表。例如{{ char_field.field.max_length }}

5.不可見欄位的特殊處理:

很多時候,我們的表單中會有一些隱藏的不可見欄位,比如honeypot。我們需要讓它在任何時候都彷彿不存在一般,比如錯誤的時候,如果你的頁面上顯示了不可見欄位的錯誤資訊,那麼使用者會很迷惑,這是哪來的呢?所以,通常,我們是不顯示不可見欄位的錯誤資訊的。
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 %}

6.重用表單模板

如果你在自己的HTML檔案中,多次使用同一個表單模板,那麼你完全可以把表單模板存成一個獨立的HTML檔案,然後在別的HTML檔案中通過include模板語法將其包含進來,如下例所示:

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

如果你的頁面同時引用了好幾個不同的表單模板,那麼為了防止衝突,你可以使用with引數,給每個表單模板取個別名,如下所示:

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

在使用的是候就是:

{% for field in comment_form %}
......