1. 程式人生 > >Django中的form元件

Django中的form元件

文章目錄


Form元件介紹

在HTML頁面中利用form表單向後端提交資料時,都會寫一些獲取使用者輸入的標籤並且用form標籤把它們包起來。

與此同時我們在好多場景下都需要對使用者的輸入做校驗,比如校驗使用者是否輸入,輸入的長度和格式等正不正確。如果使用者輸入的內容有錯誤就需要在頁面上相應的位置顯示對應的錯誤資訊。

Django form元件就實現了上面的功能。

先總結一下,form元件的主要功能如下:

  1. 生成頁面可用的HTML標籤
  2. 對使用者提交的資料進行校驗
  3. 保留上傳輸入的內容

一個簡單的註冊示例

form檔案:

from django import
forms # 按照Django form元件的要求自己寫一個類 # 且必須繼承forms.Form class RegForm(forms.Form): user = forms.CharField(label="名稱") pwd = forms.CharField(label="密碼", min_length=6)

檢視檔案:

from django.shortcuts import render, HttpResponse
from blog01 import forms  # 匯入上面的form檔案

def register(request):
    form_obj =
forms.RegForm() if request.method == 'POST': form_obj = forms.RegForm(request.POST) if form_obj.is_valid(): # 只有執行了is_valid之後,才可執行cleaned_data,否則會報錯 # cleaned_data的資料是經過校驗的 print(form_obj.cleaned_data) return HttpResponse("註冊成功!") return render(request, 'register.html', {'form': form_obj})

HTML檔案:

<body>
{# novalidate:阻止驗證(form元件會自動驗證)#}
<form action="" method="post" novalidate>
{% csrf_token %}
    {# {{ form.as_p }}  以p標籤的形式自動生成 #}
    {# 下面為簡單的自定義形式 #}
    <p>
        {{ form.user.label }}
        {{ form.user }}
        {# 錯誤提示資訊 #}
        {# {{ form.errors }}  生成form表單的所有錯誤資訊(全域性錯誤資訊)#}
        {# <span style="color: red">{{ form.user.errors }}</span>  指定欄位的所有錯誤資訊 #}
        <span style="color: red">{{ form.user.errors.0 }}</span> {# 指定欄位的所有錯誤資訊的第一個 #}
    </p>
    <p>
        {{ form.pwd.label }}
        {{ form.pwd }}
        <span style="color: red">{{ form.pwd.errors.0 }}</span>
    </p>
    <button>註冊</button>
</form>
</body>


常用欄位與外掛

from django import forms


class RegForm(forms.Form):
    user = forms.CharField(
        label="名稱",  # 用於生成label標籤或顯示內容
        # required=False,  # 為False時,允許為空
        min_length=2,  # 最小長度
        max_length=6,  # 最大長度
        initial='zyk01',  # 初始值
        error_messages={
            'required': "使用者名稱不能為空",
            'invalid': "使用者名稱格式錯誤",
            'min_length': "使用者名稱最短2位",
        }  # 自定義錯誤提示
    )

    pwd = forms.CharField(
        label="密碼",
        min_length=6,
        error_messages={
            'required': "密碼不能為空",
            'invalid': "密碼格式錯誤",
            'min_length': "密碼最短6位",
        },
        widget=forms.widgets.PasswordInput(),  # 指定input的type型別為password
    )

    # ===================================================
    #    
    # ===================================================

    """checkbox標籤(單選)"""
    keep = forms.fields.ChoiceField(
        label="記住密碼",
        initial='checked',  # 預設選中
        widget=forms.widgets.CheckboxInput(),
    )

    """checkbox標籤(多選)"""
    hobby01 = forms.fields.MultipleChoiceField(
        label="愛好01",
        choices=((1, "籃球"), (2, "足球"), (3, "乒乓球"),),
        initial=[1, 2],  # 預設選中1, 2
        widget=forms.widgets.CheckboxSelectMultiple(),
    )

    """radioSelect(等價於<input type="radio">)"""
    gender = forms.fields.ChoiceField(
        label="性別",
        choices=((1, "男"), (2, "女"), (3, "陰陽人"),),
        initial=1,  # 預設選中1
        widget=forms.widgets.RadioSelect(),
    )

    """Select標籤"""
    hobby02 = forms.fields.ChoiceField(
        choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),),
        label="愛好02",
        initial=3,  # # 預設選中3
        # widget=forms.widgets.Select(),  # 實現單選Select
        widget=forms.widgets.SelectMultiple(),  # 實現多選Select
    )

Form元件中所有內建欄位

Field
    required=True,               是否允許為空
    widget=None,                 HTML外掛
    label=None,                  用於生成Label標籤或顯示內容
    initial=None,                初始值
    help_text='',                幫助資訊(在標籤旁邊顯示)
    error_messages=None,         錯誤資訊 {'required': '不能為空', 'invalid': '格式錯誤'}
    validators=[],               自定義驗證規則
    localize=False,              是否支援本地化
    disabled=False,              是否可以編輯
    label_suffix=None            Label內容字尾
 
 
CharField(Field)
    max_length=None,             最大長度
    min_length=None,             最小長度
    strip=True                   是否移除使用者輸入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             總長度
    decimal_places=None,         小數位長度
 
BaseTemporalField(Field)
    input_formats=None          時間格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
DurationField(Field)            時間間隔:%d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      自定製正則表示式
    max_length=None,            最大長度
    min_length=None,            最小長度
    error_message=None,         忽略,錯誤資訊使用 error_messages={'invalid': '...'}
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     是否允許空檔案
 
ImageField(FileField)      
    ...
    注:需要PIL模組,pip3 install Pillow
    以上兩個字典使用時,需要注意兩點:
        - form表單中 enctype="multipart/form-data"
        - view函式中 obj = MyForm(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                選項,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               外掛,預設select外掛
    label=None,                Label內容
    initial=None,              初始值
    help_text='',              幫助提示
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查詢資料庫中的資料
    empty_label="---------",   # 預設空顯示內容
    to_field_name=None,        # HTML中value的值對應的欄位
    limit_choices_to=None      # ModelForm中對queryset二次篩選
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   對選中的值進行一次轉換
    empty_value= ''            空值的預設值
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   對選中的每一個值進行一次轉換
    empty_value= ''            空值的預設值
 
ComboField(Field)
    fields=()                  使用多個驗證,如下:即驗證最大長度20,又驗證郵箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象類,子類中可以實現聚合多個字典去匹配一個值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     檔案選項,目錄下檔案顯示在頁面中
    path,                      資料夾路徑
    match=None,                正則匹配
    recursive=False,           遞迴下面的資料夾
    allow_files=True,          允許檔案
    allow_folders=False,       允許資料夾
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支援的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1時候,可解析為192.0.2.1, PS:protocol必須為both才能啟用
 
SlugField(CharField)           數字,字母,下劃線,減號(連字元)
    ...
 
UUIDField(CharField)           uuid型別

從資料庫中獲取資料

在使用選擇標籤時,需要注意choices的選項可以從資料庫中獲取,但由於是靜態欄位,獲取的值無法實施更新,所以需要自定義構造方法從而達到此目的.

from django import forms
from blog01 import models

class RegForm(forms.Form):
    hobby03 = forms.fields.ChoiceField(
        label="愛好03",
        # choices=models.Hobby.objects.values_list('id', 'name'),
        widget=forms.widgets.CheckboxSelectMultiple(),
    )

    def __init__(self, *args, **kwargs):
        super(RegForm, self).__init__(*args, **kwargs)
        # self.fields['hobby03'].choices = ((1, 'Python'), (2, 'Django'),)
        self.fields['hobby03'].choices =  models.Hobby.objects.values_list('id','name')


校驗示例


檢驗手機號是否合法

檢視檔案:

from django.shortcuts import render, HttpResponse
from blog01 import forms  # 匯入form檔案

def register(request):
    form_obj = forms.RegForm()
    if request.method == 'POST':
        form_obj = forms.RegForm(request.POST)
        if form_obj.is_valid():
            # 只有執行了is_valid之後,才可執行cleaned_data,否則會報錯
            # cleaned_data的資料是經過校驗的
            print(form_obj.cleaned_data)
            return HttpResponse("註冊成功!")
    return render(request, 'register.html', {'form': form_obj})

HTML檔案:

<body>
{# novalidate:阻止驗證(form元件會自動驗證)#}
<form action="" method="post" novalidate>
	{% csrf_token %}
    <p>
        {{ form.phone.label }}
        {{ form.phone }}
        <span style="color: red">{{ form.phone.errors.0 }}</span>
    </p>
    <button>註冊</button>
</form>
</body>

方式一(基本操作)

form檔案:

from django import forms
from django.core.validators import RegexValidator

class RegForm(forms.Form):
    phone = forms.fields.CharField(
        label="手機號",
        validators=[
            RegexValidator(r'^1[3-9]\d{9}$', "號碼不合法")
            # RegexValidator(r"正則表示式", "錯誤提示資訊")
        ],
    )

方式二(自定義驗證規則)

form檔案:

import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError


# 自定義驗證規則
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError("手機號格式錯誤")


class RegForm(Form):
    phone = fields.CharField(
        label="手機號",
        # 使用自定義驗證規則
        # validators=[校驗器函式1, 校驗器函式2]
        validators=[mobile_validate, ],
        error_messages={'required': "手機號不能為空", },
        widget=widgets.TextInput(
            attrs={'class': 'form-control', 'placeholder': u"手機號碼"},
        )
    )

方式三(利用鉤子)

forms檔案:

import re
from django.forms import Form
from django.forms import widgets
from django.core.exceptions import ValidationError


class RegForm(Form):
    phone = forms.fields.CharField(
        label="手機號",
        error_messages={'required': "手機號不能為空", },
        widget=widgets.TextInput(
            attrs={'class': 'form-control', 'placeholder': u"手機號碼"},
        )
    )

    def clean_phone(self):
        value = self.cleaned_data.get('phone')
        mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
        if  mobile_re.match(value):
            return value
        raise ValidationError("手機號不合法")

驗證密碼一致性

檢視檔案同上⬆️

form檔案:

from django.forms import Form
from django.core.validators import RegexValidator


class RegForm(Form):
    pwd = forms.CharField(
        label="密碼",
        min_length=6,
        widget=widgets.PasswordInput(),
    )
    re_pwd = forms.CharField(
        label="確認密碼",
        min_length=6,
        widget=widgets.PasswordInput(),
    )

    def clean_re_pwd(self):
        pwd = self.cleaned_data.get('pwd')
        re_pwd = self.cleaned_data.get('re_pwd')
        if pwd == re_pwd:
            return re_pwd
        raise ValidationError("密碼不一致")

HTML檔案:

<body>
{# novalidate:阻止驗證(form元件會自動驗證)#}
<form action="" method="post" novalidate>
    {% csrf_token %}
    {# {{ form.as_p }}  以p標籤的形式自動生成 #}
    <p>
        {{ form.pwd.label }}
        {{ form.pwd }}
        <span style="color: red">{{ form.pwd.errors.0 }}</span>
    </p>
    <p>
        {{ form.re_pwd.label }}
        {{ form.re_pwd }}
        <span style="color: red">{{ form.re_pwd.errors.0 }}</span>
    </p>
    <button>註冊</button>
</form>
</body>


鉤子


區域性鉤子

from django.forms import Form


class RegForm(Form):
    test01 = forms.CharField()

    test02 = forms.CharField()

    test03 = forms.CharField()

    # 區域性鉤子
    def