Web框架django[Form]組件


新手上路

Django的Form主要具有一下幾大功能:

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

小試牛刀

1、創建Form類

# 創建一個類
from django import forms
from django.forms import fields
 
 
class DiyForm(forms.Form):
    # 類中創建字段  例如 IntegerField包含了正則表達式
    user = fields.CharField(
        max_length=18,
        min_length=6,
        required=True,
        error_messages={
            'max_length': '用戶名過長',
            'min_length': '用戶名過短',
            'required': '用戶名不能為空',
            'invalid': '輸入類型錯誤'
        }
    )
    pwd = fields.CharField(
        required=True,
        min_length=8,
        error_messages={
            'required': '密碼不可為空',
            'min_length': '密碼至少為8位'
        }
    )
    age = fields.IntegerField(
        required=True,
        error_messages={
            'required': '年齡不可為空',
            'invalid': '年齡必須為數字'
        }
    )
    email = fields.EmailField(
        required=True,
        min_length=8,
        error_messages={
            'required': '郵箱不可為空',
            'min_length': '郵箱長度不匹配',
            'invalid': '郵箱規則不符合'
        }
    )
View Code

2、View函數

from django.shortcuts import render,HttpResponse,redirect
 
def f1(request):
    if request.method == 'GET':
        obj = DiyForm()  # 實例化  傳參可進行模板渲染 生成Html代碼
        return render(request, 'f1.html', {'obj':obj})
    else:
        obj = DiyForm(request.POST)
        # 判斷是否全部驗證成功 逐一交給類字段里面一一進行驗證、像一層濾網
        if obj.is_valid():
            # 用戶提交的數據   驗證成功的信息
            print('驗證成功', obj.cleaned_data)
            return redirect('http://www.baidu.com')
        else:
            print('驗證失敗', obj.errors)  # 封裝的錯誤信息
            return render(request, 'f1.html', {'obj': obj})
View Code

3、Html生成

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>DjangoForm</title>
</head>
<body>
<form action="/f1.html" method="post" novalidate enctype="multipart/form-data">
    <p>{{ obj.user }}{{ obj.errors.user.0 }}</p>
    <p>{{ obj.pwd }}{{ obj.errors.pwd.0 }}</p>
    <p>{{ obj.age }}{{ obj.errors.age.0 }}</p>
    <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
    <input type="submit" value="提交">
</form>
</body>
</html>
View Code

初露鋒芒

創建Form類時,主要涉及到 【字段】 和 【插件】,字段用於對用戶請求數據的驗證,插件用於自動生成HTML;

1、Django中Form類內置字段如下:

常用字段

用於保存正則表達式
ChoiceField *****
MultipleChoiceField
CharField
IntegerField
DecimalField
DateField
DateTimeField
EmailField
GenericIPAddressField
FileField
 
RegexField
View Code

詳細字段

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                   是否移除用戶輸入空白
  
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類型
View Code

2、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
View Code

3、常用選擇插件

# 單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
# )
View Code

展露頭角

單選或者多選時、數據源是否可以實時更新、、、

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

方式一、

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.ChoiceField(
        # choices=((1, '上海'), (2, '北京'),),
        initial=2,
        widget=widgets.Select
    )
  
    def __init__(self, *args, **kwargs):
        super(MyForm,self).__init__(*args, **kwargs)
        # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
        #
        self.fields['user'].widget.choices = models.Classes.objects.all().value_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())

更上一層樓

通過上述,Django的Form組件提供驗證用戶提交的數據並可以顯示錯誤信息(或自定制),更能可以生成相應的Html代碼。更是猜想到,僅僅根據Form組件的驗證或許滿足不了一些需求,於是建立再Form的驗證功能上使其有很強的擴展性

一、基於Form組件的字段上的簡單擴展

方式A

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
 
class MyForm(Form):
    phone = fields.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^188[0-9]+$', '數字必須以188開頭')],

方式B

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 PublishForm(Form):
  
  
    title = fields.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required': '標題不能為空',
                                            'min_length': '標題最少為5個字符',
                                            'max_length': '標題最多為20個字符'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': '標題5-20個字符'}))
  
  
    # 使用自定義驗證規則
    phone = fields.CharField(validators=[mobile_validate, ],
                            error_messages={'required': '手機不能為空'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'手機號碼'}))
  
    email = fields.EmailField(required=False,
                            error_messages={'required': u'郵箱不能為空','invalid': u'郵箱格式錯誤'},
                            widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'郵箱'}))

二、基於源碼執行的流程上進行擴展

方式A

例如在注冊一個賬號時、通過Form的驗證其賬號符合規則時,還將要判斷該賬號是否存在於數據庫,如存在則肯定是注冊不通過的

自定義方法、單一字段逐個再次驗證

from Formtest import models
from django import forms
from django.forms import fields
from django.forms import widgets
from django.core.exceptions import ValidationError,NON_FIELD_ERRORS
from django.core.validators import RegexValidator
 
 
class AjaxForm(forms.Form):
    user=fields.CharField(
        max_length=10,
        required=False,
        validators=[RegexValidator(r'^[a-z]+$', 'Enter a valid extension.', 'invalid')],
    )
    email=fields.EmailField()
 
    def clean_user(self):
        """
        Form中字段中定義的格式匹配完之后,執行此方法進行驗證
        :return:
        """
        v = self.cleaned_data['user']
        if models.UserInfo.objects.filter(user=v).count():
            raise ValidationError('此用戶名已經存在')
        return v
 
    def clean_email(self):
        """
        email驗證過之后、可以自定義驗證該郵箱是否被使用...
        :return:
        """
        pass

方式B

from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
class AjaxForm(forms.Form):
    username = fields.CharField()
    user_id = fields.IntegerField(
        widget=widgets.Select(choices=[(0,'alex'),(1,'劉皓宸'),(2,'楊建'),])
    )
    # 自定義方法 clean_字段名,設置一個字段整體驗證
    # 必須返回值self.cleaned_data['username']
    # 如果出錯:raise ValidationError('用戶名已存在')
    def clean_username(self):
        v = self.cleaned_data['username']
        #數據庫中如果有這用戶
        if UsersF.objects.filter(username=v).count():
            # 整體錯了
            # 自己詳細錯誤信息
            raise ValidationError('用戶名已存在')
        return v

    def clean_user_id(self):
        return self.cleaned_data['user_id']

    #設置整體錯誤,可以用來設置多個字段整體驗證
    def clean(self):
        value_dict=self.cleaned_data
        v1 = value_dict.get('username')
        v2 = value_dict.get('user_id')
        if v1=='root' and v2==0:
            raise ValidationError('整體錯誤信息')
        return self.cleaned_data


def ajax(request):
    if request.method == 'GET':
        obj = AjaxForm()
        return render(request,'ajax.html',{'obj':obj})
    else:
        ret = {'status':'tom','message':None}
        import json
        obj = AjaxForm(request.POST)
        if obj.is_valid():
            # 跳轉到百度
            # return redirect('http://www.baidu.com')
            # if ....
            #     obj.errors['username'] = ['用戶名已經存在',]
            # if ....
            #     obj.errors['email'] = ['用戶名已經存在',]

            ret['status'] = ''
            return HttpResponse(json.dumps(ret))
        else:
            # print(type(obj.errors))
            # print(obj.errors)
            from django.forms.utils import ErrorDict
            # print(obj.errors.as_ul())
            # print(obj.errors.as_json())
            # print(obj.errors.as_data())


            ret['message'] = obj.errors
            # 錯誤信息顯示在頁面上
            return HttpResponse(json.dumps(ret))

 

 

select

from django.shortcuts import render
from django import forms
from django.forms import fields
from django.forms import widgets
class TestForm(forms.Form):
    user = fields.CharField(
        required=True, # 是否必填
        max_length=12, # 最大長度
        min_length=3,  # 最小長度
        error_messages={}, # 錯誤提示
        #widget = widgets.Select(), # 定制HTML插件
        label='用戶名',
        initial='請輸入用戶',
        help_text='asdfasdf',
        show_hidden_initial=False,
        # validators=[]
        disabled=True,
        label_suffix='->'
    )
    age = fields.IntegerField(
        label='年齡',
        max_value= 12,
        min_value=5,
        error_messages={
            'max_value':'太大了'
        }
    )

    email = fields.EmailField(
        label='郵箱'
    )

    img = fields.FileField()

    city = fields.TypedChoiceField(
        coerce=lambda x: int(x),
        choices=[(1,'上海',),(2,'北京'),(3,'沙河'),],
        initial=2
    )

    xoo = fields.FilePathField(
        path='app01'
    )


    #多選checkbox
    c = fields.MultipleChoiceField(
        choices=[(1, '剛娘'), (2, '鐵娘'), (3, '鋼彈')],
        initial=[1,3],
        widget=widgets.CheckboxSelectMultiple


    )


    #多選select
    bobby = fields.MultipleChoiceField(
        choices=[(1, '剛娘'), (2, '鐵娘'), (3, '鋼彈')],
        initial=[1, 2],
        widget=widgets.SelectMultiple
    )

    #單選select
    t = fields.CharField(
        widget = widgets.Select(choices=[(1,'上海',),(2,'北京'),(3,'沙河'),]),
        initial=2

    )



    #**********重要*****
    u = fields.CharField(
        widget= widgets.Select()
    )
    def __init__(self,*args,**kwargs):
        #實時更新,拷貝所有的靜態字段,復制給self.fields
        super(TestForm,self).__init__(*args,**kwargs)
        self.fields['u'].widget.choices=UsersF.objects.values_list('id','username')

def test(request):
    if request.method == 'GET':
        obj = TestForm()
        return render(request,'test.html',{'obj':obj})
    elif request.method=='POST':
        obj = TestForm(request.POST,request.FILES)

        if obj.is_valid():
            print('成功',obj.cleaned_data)
            return render(request, 'test.html', {'obj': obj})
        else:
            pass

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM