天天看點

第十三課 Django 表單模型(一)

表單模型(一)

Django的Form表單類與Django模型描述對象的邏輯結構、行為以及它呈現給我們内容的形式的方式大緻相同

假設你想從表單接收使用者名資料,一般情況下,你需要在HTML中手動編寫一個如下的表單元素:

<form action="/student/" method="post">
    <label for="name">姓名: </label>
    <input id="name" type="text" name="name" value="{{ name }}">
    <input type="submit" value="送出">
</form>
           

1. 編寫表單類

我們可以通過Django提供的Form類來生成上面的表單,不再需要手動在HTML中編寫

首先,在你目前app内建立一個

forms.py

檔案,然後輸入下面的内容:

from django import forms

class NameForm(forms.Form):
    your_name = forms.CharField(label='name', max_length=100)
           
  • 所有的表單類都要繼承forms.Form類
  • max_length

    限制最大長度為100。它同時起到兩個作用,一是在浏覽器頁面限制使用者輸入不可超過100個字元,二是在後端伺服器驗證使用者輸入的長度不可超過100

每個Django表單的執行個體都有一個内置的

is_valid()

方法,用來驗證接收的資料是否合法。如果所有資料都合法,那麼該方法将傳回True,并将所有的表單資料轉存到它的一個叫做

cleaned_data

的屬性中,該屬性是一個字典類型資料。

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

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

一定要注意,它不包含

<form>

标簽本身以及送出按鈕!!!為什麼要這樣?友善你自己控制表單動作和CSS,JS以及其它類似bootstrap架構的嵌入!

2. 視圖處理

需要在視圖中,執行個體化我們編寫好的表單類

# 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 = NameForm(request.POST)
        # 驗證資料是否合法
        if form.is_valid():
            # 處理form.cleaned_data中的資料
            return HttpResponse('hello world

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

    return render(request, 'name.html', {'form': form})
           
  • 對于GET方法請求頁面時,傳回空的表單,讓使用者可以填入資料
  • 對于POST方法,接收表單資料,并驗證

可以通過表單的

is_bound

屬性可以獲知一個表單已經綁定了資料,還是一個空表

3. 模闆處理

在Django的模闆中,我們隻需要按下面處理,就可以得到完整的HTML頁面,但是

submit

按鈕需要自己定義:

<form action="/student/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="送出" />
</form>
           
  • 使用POST的方法時,必須添加

    {% csrf_token %}

    标簽,用于處理csrf安全機制
  • {{ form }}

    Django會為你生成其它所有的form标簽元素
  • 送出按鈕需要手動添加!

4. 表單字段

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

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)
           

這個例子就有4個框組了。實際上Django的表單子產品為我們内置了許多表單字段,如下所示:

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

每一個表單字段類型都對應一種Widget類,每一種Widget類都對應了HMTL語言中的一種input元素類型,比如

<input type="text">

。需要在HTML中實際使用什麼類型的input,就需要在Django的表單字段中選擇相應的field。比如要一個

<input type="text">

,可以選擇一個

CharField

一旦你的表單接收資料并驗證通過了(

is_valid()

),那麼就可以從

form.cleaned_data

字典中讀取所有的表單資料,并且已經轉化為恰當的Python類型。例子:

# 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 = ['[email protected]']
    if cc_myself:
        recipients.append(sender)

    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/')
           

5. 使用表單模闆

① 表單渲染格式

前面我們通過

{{ form }}

模闆語言,簡單地将表單渲染到HTML頁面中了,實際上,有更多的方式:

  • {{ form.as_table }}

    将表單渲染成一個表格元素,每個輸入框作為一個

    <tr>

    标簽
  • {{ form.as_p }}

    将表單的每個輸入框包裹在一個

    <p>

    标簽内 tags
  • {{ form.as_ul }}

    将表單渲染成一個清單元素,每個輸入框作為一個

    <li>

    标簽

注意:你要自己手動編寫

<table>

<ul>

标簽。

下面是将上面的ContactForm作為

{{ form.as_p }}

的渲染出的html:

<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參數。

② 手動渲染表單字段

直接

{{ form }}

雖然好,啥都不用操心,但是往往并不是你想要的,比如你要使用CSS和JS,比如你要引入Bootstarps架構,這些都需要對表單内的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>
           

③ 渲染表單錯誤資訊

注意上面的例子中,我們使用

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

④ 循環表單的字段

如果你的表單字段有相同格式的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.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 }}

⑤ 不可見字段

很多時候,我們的表單中會有一些隐藏的不可見的字段,我們需要讓它在任何時候都仿佛不存在一般,比如有錯誤的時候,如果你在頁面上顯示了不可見字段的錯誤資訊,那麼使用者會很迷惑,這是哪來的呢?是以,通常我們是不顯示不可見字段的錯誤資訊的。

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 %}
           

⑥ 重用表單模闆

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

# 實際的頁面檔案中:
{% include "form_snippet.html" %}

-----------------------------------------------------

# 單獨的表單模闆檔案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 %}
......