1. 程式人生 > >Python Web框架篇:Django Form組件

Python Web框架篇:Django Form組件

multipart ima class mage 決定 red ttr 提示 echo

Form簡介

在HTTP中,表單(form標簽),是用來提交數據的,其action屬性說明了其傳輸數據的方法:如何傳、如何接收。

訪問網站時,表單可以實現客戶端與服務器之間的通信。例如查詢,就用到了表單(其屬性中,action=get)。

再比如說註冊與登陸,也是要用到表單的。但這裏由於涉及到隱私問題,需要保證數據傳輸的安全性,因此其傳輸方法就應當使用post而非get。

總之,對客戶端來說,表單就是用來向服務器提交數據的;

而對服務器來說,表單就是你提供給客戶端的發送信息的渠道,你需要對用戶發送來的信息進行處理和響應,以達到頁面的交互。

Django Form的功能

  • 生成HTML標簽
  • 驗證用戶數據(顯示錯誤信息)
  • HTML Form提交保留上次提交數據
  • 初始化頁面顯示內容

Django Form驗證流程

用於驗證用戶請求數據合法性的一個組件
如果沒有Form就得繁瑣地用正則表達式了

a. 用戶提交數據的驗證
  1、創建模版            class LoginForm(forms.Form):...
  2、將請求交給模版,創建一個對象 obj = LoginForm(request.POST)
  3、進行驗證           obj.is_valid()
  4、獲取正確信息          obj.clean()
  5、獲取錯誤信息          obj.errors
b. 錯誤信息提示


  Form提交,刷新頁面的特性,模版對象內部值豐富,再顯示時,值和錯誤信息都有
c. 保留上一次提交的數據
  1、自動生成html標簽
  2、保留上一次提交的數據


Form的創建

 1 from django.shortcuts import render, redirect
 2 from django import forms
 3 #這裏為了簡單,把form類寫到視圖函數
 4 # 模版
 5 class LoginForm(forms.Form):
 6     # 模版中的元素
 7
user = forms.CharField(min_length=6,error_messages={"required": 用戶名不能為空,min_length: 用戶名長度不能小6}) 8 email = forms.EmailField(error_messages={"required": 郵箱不能為空,invalid: 郵箱格式錯誤})
9 def login(request): 10 if request.method == "GET": 11 # 數據庫中獲取 12 obj = LoginForm() 13 return render(request,login.html,{oo: obj}) 14 elif request.method == "POST": 15 """ 16 obj = LoginForm(request.POST) 17 # 驗證 18 status = obj.is_valid() 19 print(status) 20 21 value_dict = obj.clean() 驗證成功的值,輸入格式正確的拿到了 22 print(value_dict) 23 24 # error_obj = obj.errors 25 error_obj = obj.errors.as_json() 26 print(error_obj) code定義錯誤信息 27 """ 28 obj = LoginForm(request.POST) 29 if obj.is_valid(): 30 value_dict = obj.clean() 31 print(value_dict) 32 # create(**value_dict) 33 else: 34 # 封裝了所有的錯誤信息 35 # error_obj=obj.errors 36 # print(obj.errors[‘email‘][0],type(error_obj[‘email‘]) 37 # print(obj.errors[‘email‘][0],type(error_obj[‘email‘][0]) 38 # print(obj.errors["user"][0]) 39 # print(type(error_obj)) 40 from django.forms.utils import ErrorDict 41 pass 42 return render(request, login.html,{oo: obj})

當然你也可以在app裏創建form.py文件,在裏面創建類:

下文將使用這個文件。

from django import forms as DForms
from django.forms import fields
from django.forms import widgets
from django.core.validators import RegexValidator
class DetailForm(DForms.Form):
    user1 = fields.CharField() #默認input框,可以改成select,radio等
    user2 = fields.CharField(widget=widgets.TextInput)
    user4 = fields.IntegerField()

    # 字符串
    user3 = fields.ChoiceField(choices=[(1, SH), (2, BJ), ])

    user5 = fields.CharField(
        widget=widgets.Select(choices=[(1, SH), (2, BJ), ])
    )
    user6 = fields.IntegerField(
        widget=widgets.Select(choices=[(1, SH), (2, BJ), ])
    )
    user7 = fields.IntegerField(
        widget=widgets.RadioSelect(choices=[(1, SH), (2, BJ), ])
    )
    #{‘user1‘: ‘11‘, ‘user2‘: ‘22‘, ‘user4‘: 33, ‘user3‘: ‘1‘, ‘user5‘: ‘2‘, ‘user6‘: 1, ‘user7‘: 2}

視圖函數:

from django.shortcuts import render, redirect


from app01 import forms


def detail(request):
    obj = forms.DetailForm(request.POST)
    obj.is_valid()
    print(obj.clean())
    return render(request,detail.html, {obj: obj})

生成HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/detail.html" method="POST">
    {{ obj.user1 }}
    {{ obj.user2 }}
    {{ obj.user3 }}
    {{ obj.user4 }}
    {{ obj.user5 }}
    {{ obj.user6 }}
    {{ obj.user7 }}
        <input type="submit" value="提交"/>
    </form>
</body>
</html>

常用HTML標簽:

 1   <form method="POST" enctype="multipart/form-data">
 2         {% csrf_token %}
 3         
 4             {{ form.xxoo.label }}
 5             {{ form.xxoo.id_for_label }}
 6             {{ form.xxoo.label_tag }}
 7             {{ form.xxoo.errors }}
 8             <p>{{ form.user }} {{ form.user.errors }}</p>
 9             <input type="submit" />
10     </form>

運行後後臺返回結果:

#{‘user1‘: ‘11‘, ‘user2‘: ‘22‘, ‘user4‘: 33, ‘user3‘: ‘1‘, ‘user5‘: ‘2‘, ‘user6‘: 1, ‘user7‘: 2}
下面將講解為什麽是這個結果

Django的Form的實現步驟:
a. 創建一個驗證用戶請求的模板包含:

類:模版,到底驗證幾個
字段:用於驗證用戶某個字段
插件:user = forms.CharField(..,widget=Input框)

Django內置字段如下:

1,字段核心參數

每個字段類的構造函數至少接受這些參數。有些字段類接受額外的、字段特有的參數,但以下參數應該總是能接受:

Field.required
默認情況下,每個字段 類都假設必需有值,所以如果你傳遞一個空的值 —— 不管是None 還是空字符串(“”) —— clean() 將引發一個ValidationError 異常。

Field.label
正如在前面“輸出表單為HTML”中解釋的,字段默認label 是通過將字段名中所有的下劃線轉換成空格並大寫第一個字母生成的。如果默認的標簽不合適,可以指定label。

Field.label_suffix
label_suffix 參數讓你基於每個字段覆蓋表單的label_suffix

Field.initial
initial 參數讓你指定渲染未綁定的表單中的字段時使用的初始值。

Field.widget
widget 參數讓你指定渲染表單時使用的Widget 類。更多信息參見Widgets。

Field.help_text
help_text 參數讓你指定字段的描述文本。如果提供help_text,在通過表單的便捷方法(例如,as_ul())渲染字段時,它將緊接著字段顯示。

Field.error_messages
error_messages 參數讓你覆蓋字段引發的異常中的默認信息。傳遞的是一個字典,其鍵為你想覆蓋的錯誤信息。

Field.validators
validators 參數讓你可以為字段提供一個驗證函數的列表。

Field.localize
localize 參數啟用表單數據的本地化,包括輸入和輸出。

Field.has_changed()
has_changed() 方法用於決定字段的值是否從初始值發生了改變。返回True 或False。

2,內建字段

Field
    required=True,               是否允許為空
    widget=None,                 HTML插件
    label=None,                  用於生成Label標簽或顯示內容
    initial=None,                初始值
    help_text=‘‘,                幫助信息(在標簽旁邊顯示)
    error_messages=None,         錯誤信息 {required: 不能為空, invalid: 格式錯誤}
    show_hidden_initial=False,   是否在當前插件後面再加一個隱藏的且具有默認值的插件(可用於檢驗兩次輸入是否一直)
    validators=[],               自定義驗證規則
    localize=False,              是否支持本地化
    disabled=False,              是否可以編輯
    label_suffix=None            Label內容後綴
 
 
CharField(Field)
    max_length=None,             最大長度
    min_length=None,             最小長度
    strip=True                   是否移除用戶輸入空白
  • 默認的Widget:TextInput
  • 空值:’ ‘(一個空字符串)
  • 規範化為:一個Unicode 對象。
  • 如果提供,驗證max_length 或min_length。 否則,所有的輸入都是合法的。
  • 錯誤信息的鍵:required, max_length, min_length

  可選參數:max_length, min_length

IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
  • 默認的Widget:當 Field.localize 是False 時為NumberInput,否則為TextInput。
  • 空值:None
  • 規範化為:一個Float 對象。
  • 驗證給出的值是一個浮點數。和Python 的float() 函數一樣,允許前導和尾隨的空白符。
  • 錯誤信息的鍵:required, invalid, max_value, min_value
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
  • 默認的Widget:TextInput
  • 空值:None
  • 規範化為:一個Python timedelta。
  • 驗證給出的值是一個字符串,而可以給轉換為timedelta。
  • 錯誤信息的鍵:required, invalid.
RegexField(CharField)
    regex,                      自定制正則表達式
    max_length=None,            最大長度
    min_length=None,            最小長度
    error_message=None,         忽略,錯誤信息使用 error_messages={invalid: ...}
 
EmailField(CharField)
  • 默認的Widget:EmailInput
  • 空值:”(一個空字符串)
  • 規範化為:一個Unicode 對象。
  • 驗證給出的值是一個合法的郵件地址,使用一個適度復雜的正則表達式。
  • 錯誤信息的鍵:required, invalid

 
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)
  • 默認的Widget:URLInput
  • 空值:”(一個空字符串)
  • 規範化為:一個Unicode 對象。
  • 驗證給定值是有效的URL。
  • 錯誤信息的鍵:required, invalid
BooleanField(Field)  
  • 默認的Widget:CheckboxInput
  • 空值:False
  • 規範化為:Python 的True 或 False。
  • 如果字段帶有required=True,驗證值是否為True(例如復選框被勾上)。
  • 錯誤信息的鍵:required
NullBooleanField(BooleanField)
  • 默認的Widget:NullBooleanSelect
  • 空值:None
  • 規範化為:一個Python True, False 或None 值。
  • 不驗證任何內容(即,它從不引發ValidationError)。
ChoiceField(Field)
    ...
    choices=(),                選項,如:choices = ((0,上海),(1,北京),)
    required=True,             是否必填
    widget=None,               插件,默認select插件
    label=None,                Label內容
    initial=None,              初始值
    help_text=‘‘,              幫助提示
  • 默認的Widget:Select
  • 空值:”(一個空字符串)
  • 規範化為:一個Unicode 對象。
  • 驗證給定的值在選項列表中存在。
  • 錯誤信息的鍵:required, invalid_choice
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

MultipleChoiceField(**kwargs)

  • 默認的Widget:SelectMultiple
  • 空值:[](一個空列表)
  • 規範化為:一個Unicode 對象列表。
  • 驗證給定值列表中的每個值都存在於選擇列表中。
  • 錯誤信息的鍵:required, invalid_choice, invalid_list

TypedMultipleChoiceField(**kwargs)
就像MultipleChoiceField,除了TypedMultipleChoiceField需要兩個額外的參數,coerce和empty_value。


    • 默認的Widget:SelectMultiple
    • 空值:empty_value
    • 規範化為:coerce參數提供的類型值列表。
    • 驗證給定值存在於選項列表中並且可以強制。
    • 錯誤信息的鍵:required, invalid_choice

TypedChoiceField(ChoiceField) coerce
= lambda val: val 對選中的值進行一次轉換 empty_value= ‘‘ 空值的默認值
  • 默認的Widget:Select
  • 空值:empty_value
  • 規範化為:coerce 參數類型的值。
  • 驗證給定的值在選項列表中存在並且可以被強制轉換。
  • 錯誤信息的鍵:required, invalid_choice

ImageField(**kwargs)

  • 默認的Widget:ClearableFileInput
  • 空值:None
  • 規範化為: An UploadedFile object that wraps the file content and file name into a single object.
  • 驗證文件數據已綁定到表單,並且該文件具有Pillow理解的圖像格式。
  • 錯誤信息的鍵:required, invalid, missing, empty, invalid_image

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=‘‘
  • path,你想要列出的目錄的絕對路徑。這個目錄必須存在。
  • recursive,如果為False(默認值),只用直接位於path 下的文件或目錄作為選項。如果為True,將遞歸訪問這個目錄,其所有的子目錄和文件都將作為選項。
  • match,正則表達式表示的一個模式;只有匹配這個表達式的名稱才允許作為選項。
  • allow_files,可選。為True 或False。默認為True。表示是否應該包含指定位置的文件。它和allow_folders 必須有一個為True。
  • allow_folders,可選。為True 或False。 默認為False。表示是否應該包含指定位置的目錄。 它和allow_files 必須有一個為True。
FileField(**kwargs)
  • 默認的Widget:ClearableFileInput
  • 空值:None
  • 規範化為:一個UploadedFile 對象,它封裝文件內容和文件名為一個單獨的對象。
  • 可以驗證非空的文件數據已經綁定到表單。
  • 錯誤信息的鍵:required, invalid, missing, empty, max_length

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類型
  • 默認的Widget:TextInput
  • 空值:”(一個空字符串)
  • 規範化為:一個UUID 對象。
  • 錯誤信息的鍵:required, invalid
 

3.處理關系字段

兩個字段可用於表示模型之間的關系:ModelChoiceField和ModelMultipleChoiceField。這兩個字段都需要單個queryset參數,用於創建字段的選擇。在表單驗證時,這些字段將把一個模型對象(在ModelChoiceField的情況下)或多個模型對象(在ModelMultipleChoiceField的情況下)放置到cleaned_data表單的字典。

對於更復雜的用法,可以在聲明表單字段時指定queryset=None,然後在窗體的init()方法中填充queryset:

ModelChoiceField(**kwargs)

  • 默認的Widget:Select
  • 空值:None
  • 規範化為:一個模型實例。
  • 驗證給定的id存在於查詢集中。
  • 錯誤信息的鍵:required, invalid_choice

ModelMultipleChoiceField(**kwargs)

  • 默認的Widget:SelectMultiple
  • 空值:QuerySet (self.queryset.none())
  • 規範化為: 模型實例的一個QuerySet。
  • 驗證在給定的值列表中的每個id存在於查詢集中。
  • 錯誤信息的鍵:required, list, invalid_choice, invalid_pk_value  

Django內置插件:

TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
# 單radio,值為字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.RadioSelect(choices=((1,‘上海‘),(2,‘北京‘),))
# )
 
# 單radio,值為字符串
# user = fields.ChoiceField(
#     choices=((1, ‘上海‘), (2, ‘北京‘),),
#     initial=2,
#     widget=widgets.RadioSelect
# )
 
# 單select,值為字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.Select(choices=((1,‘上海‘),(2,‘北京‘),))
# )
 
# 單select,值為字符串
# user = fields.ChoiceField(
#     choices=((1, ‘上海‘), (2, ‘北京‘),),
#     initial=2,
#     widget=widgets.Select
# )
 
# 多選select,值為列表
# user = fields.MultipleChoiceField(
#     choices=((1,‘上海‘),(2,‘北京‘),),
#     initial=[1,],
#     widget=widgets.SelectMultiple
# )
 
 
# 單checkbox
# user = fields.CharField(
#     widget=widgets.CheckboxInput()
# )
 
 
# 多選checkbox,值為列表
# user = fields.MultipleChoiceField(
#     initial=[2, ],
#     choices=((1, ‘上海‘), (2, ‘北京‘),),
#     widget=widgets.CheckboxSelectMultiple
# )

在使用選擇標簽時,需要註意choices的選項可以從數據庫中獲取,但是由於是靜態字段 ***獲取的值無法實時更新***,那麽需要自定義構造方法從而達到此目的。

如果內建的字段不能滿足你的需求,你可以很容易地創建自定義的字段。你需要創建django.forms.Field 的一個子類。它只要求實現一個clean() 方法和接收上面核心參數的init() 方法(required, label, initial, widget, help_text)。

#方法一:
from
app01 import models class DBForm(DForms.Form): host = fields.CharField() host_type = fields.IntegerField( widget=widgets.Select(choices=[]) ) def __init__(self,*args, **kwargs): # 執行父類的構造方法 super(DBForm,self).__init__(*args, **kwargs) self.fields[host_type].widget.choices = models.UserType.objects.all().values_list(id,caption)
#方法二:使用django提供的ModelChoiceField和ModelMultipleChoiceField字段來實現
from django import forms
from django.forms import fields from django.forms import widgets from django.forms import models as form_model from django.core.exceptions import ValidationError from django.core.validators import RegexValidator class FInfo(forms.Form): authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())

自定義Form驗證規則:

技術分享
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator

#方法一
class MyForm(Form):
    user = fields.CharField(
        validators=[RegexValidator(r^[0-9]+$, 請輸入數字), RegexValidator(r^159[0-9]+$, 數字必須以159開頭)],
    )

# 方法二
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(手機號碼格式錯誤)
phone = fields.CharField(validators=[mobile_validate, ],
                            error_messages={required: 手機不能為空},
                            widget=widgets.TextInput(attrs={class: "form-control",
                                                          placeholder: u手機號碼}))

#方法三
from django import forms
    from django.forms import fields
    from django.forms import widgets
    from django.core.exceptions import ValidationError
    from django.core.validators import RegexValidator
 
    class FInfo(forms.Form):
        username = fields.CharField(max_length=5,
                                    validators=[RegexValidator(r^[0-9]+$, Enter a valid extension., invalid)], )
        email = fields.EmailField()
 
        def clean_username(self):
            """
            Form中字段中定義的格式匹配完之後,執行此方法進行驗證
            :return:
            """
            value = self.cleaned_data[username]
            if "666" in value:
                raise ValidationError(666已經被玩爛了..., invalid)
            return value
View Code

同時生成多個標簽驗證:

from django.forms import Form
from django.forms import widgets
from django.forms import fields
 
from django.core.validators import RegexValidator
 
 
############## 自定義字段 ##############
class PhoneField(fields.MultiValueField):
    def __init__(self, *args, **kwargs):
        # Define one message for all fields.
        error_messages = {
            incomplete: Enter a country calling code and a phone number.,
        }
        # Or define a different message for each field.
        f = (
            fields.CharField(
                error_messages={incomplete: Enter a country calling code.},
                validators=[
                    RegexValidator(r^[0-9]+$, Enter a valid country calling code.),
                ],
            ),
            fields.CharField(
                error_messages={incomplete: Enter a phone number.},
                validators=[RegexValidator(r^[0-9]+$, Enter a valid phone number.)],
            ),
            fields.CharField(
                validators=[RegexValidator(r^[0-9]+$, Enter a valid extension.)],
                required=False,
            ),
        )
        super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args,
                                         **kwargs)
 
    def compress(self, data_list):
        """
        當用戶驗證都通過後,該值返回給用戶
        :param data_list:
        :return:
        """
        return data_list

Python Web框架篇:Django Form組件