1. 程式人生 > >Django之form表單

Django之form表單

所有 install rip mod 由於 合格 NPU enc 多選框

Form介紹

總結,form組件的主要功能如下:

  生成頁面可用的HTML標簽

  對用戶提交的數據進行校驗

  保留上次輸入內容

  提示錯誤信息

普通方式手寫註冊功能

views.py

def register(request):
    name_error = ‘‘
    if request.method == ‘POST‘:
        user = request.POST.get(‘user‘)
        pwd = request.POST.get(‘pwd‘)
        if len(user) < 6:
            name_error = ‘用戶名太短了‘
        else:
            return HttpResponse(‘註冊成功‘)
    return render(request, ‘register.html‘, {‘name_error‘: name_error})

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css">
    <link rel="stylesheet" href="/static/font-awesome-4.7.0/css/font-awesome.css">
    <link rel="stylesheet" href="/static/bootstrap-sweetalert-master/dist/sweetalert.css">
    {% load static %}
    <script src="{% static ‘bootstrap-sweetalert-master/dist/sweetalert.js‘ %}"></script>
    <script src="{% static ‘jquery-3.3.1.min.js‘ %}"></script>
    <script src="{% static ‘bootstrap-3.3.7/js/bootstrap.js‘ %}"></script>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    <p>
        用戶名:<input type="text" name="user"><span style="color: red">{{ name_error }}</span>
    </p>
    <p>
        密碼:<input type="password" name="pwd">
    </p>
    <button type="submit">提交</button>
</form>
</body>
</html>

使用form組件實現註冊功能

views.py

  先定義好一個RegForm類:

from django import forms


# 按照Django form組件的要求自己寫一個類
class RegForm(forms.Form):
    user = forms.CharField(label=‘用戶名‘)
    pwd = forms.CharField(label=‘密碼‘)

  再寫一個視圖函數:

# 使用form組件實現註冊方式
def register2(request):
    form_obj = RegForm()
    if request.method == ‘POST‘:
        # 實例化form對象的時候,把post提交過來的數據直接傳進去
        form_obj = RegForm(request.POST)
        # 調用form_obj校驗數據的方法
        if form_obj.is_valid():
            return HttpResponse(‘註冊成功‘)
    return render(request, ‘register2.html‘, {‘form_obj‘: form_obj})

  進階版視圖函數:

from django import forms
from django.forms import widgets
from app01 import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
import re

def check_name(value):
    if ‘alex‘ in value:
        raise ValidationError(‘不符合社會主義核心價值觀‘)


# 定義form
class RegForm(forms.Form):
    user = forms.CharField(
        label=‘用戶名‘,
        required=False,  # 不允許為空
        min_length=6,  # 最小長度為6
        initial=‘wjs521‘,  # 初識值為wjs521
        disabled=False,  # 不可以編輯
        validators=[check_name],
        error_messages={
            ‘min_length‘: ‘你的長度太短了,還不到6‘,
            ‘required‘: ‘不能為空‘
        }
    )
    #
    pwd = forms.CharField(
        label=‘密碼‘,
        min_length=6,
        widget=widgets.PasswordInput()  # HTML插件,頁面顯示密碼時是密文的
    )
    re_pwd = forms.CharField(
        label=‘確認密碼‘,
        min_length=6,
        widget=widgets.PasswordInput()
    )

    #
    # gender = forms.ChoiceField(
    #     choices=((1, ‘男‘), (2, ‘女‘), (3, ‘不詳‘)),
    #     widget=widgets.CheckboxSelectMultiple()  # 可以進行多選的多選框
    # )
    #
    # hobby = forms.ChoiceField(
    #     # choices=((1, ‘足球‘), (2, ‘籃球‘), (3, ‘雙色球‘)),
    #     choices=models.Hobby.objects.all().values_list(‘id‘, ‘name‘),
    #     widget=widgets.SelectMultiple()  # 可以多個選擇
    # )

    phone = forms.CharField(
        label=‘手機號‘,
        # validators=[
        #     RegexValidator(r‘^1[3-9]\d{9}$‘, ‘手機號不正經‘)
        # ]
    )

    # def __init__(self, *args, **kwargs):
    #     super().__init__(*args, **kwargs)
    #     self.fields[‘hobby‘].choices = models.Hobby.objects.all().values_list(‘id‘, ‘name‘)

    # def clean_phone(self):
    #     value = self.cleaned_data.get(‘phone‘)
    #     if re.match(r‘^1[3-9]\d{9}$‘,value):
    #         return value
    #
    #     raise ValidationError(‘手機號不正經‘)

    # 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(‘兩次密碼不一致‘)

    def clean(self):
        pwd = self.cleaned_data.get(‘pwd‘)
        re_pwd = self.cleaned_data.get(‘re_pwd‘)

        if pwd == re_pwd:
            return self.cleaned_data
        self.add_error(‘re_pwd‘,‘兩次密碼不一致‘)
        raise ValidationError(‘兩次密碼不一致‘)
from django.shortcuts import render, HttpResponse
from app01.forms import RegForm


def register(request):
    name_error = ‘‘
    if request.method == ‘POST‘:
        user = request.POST.get(‘user‘)
        pwd = request.POST.get(‘pwd‘)

        if len(user) < 6:
            name_error = ‘你太短了‘

        else:
            # 數據時合格的 進行數據的操作
            return HttpResponse(‘註冊成功‘)

    return render(request, ‘register.html‘, {"name_error": name_error})


def register2(request):
    form_obj = RegForm()
    # print(RegForm.hobby.choices)
    # print(11,form_obj.fields[‘hobby‘].choices)

    if request.method == ‘POST‘:
        form_obj = RegForm(request.POST)

        if form_obj.is_valid():
            # cleaned_data  是經過校驗的數據
            print(form_obj.cleaned_data)
            # 數據操作
            return HttpResponse(‘註冊成功‘)

    return render(request, ‘register2.html‘, {"form_obj": form_obj})

register2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>註冊2</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css">
    <link rel="stylesheet" href="/static/font-awesome-4.7.0/css/font-awesome.css">
    <link rel="stylesheet" href="/static/bootstrap-sweetalert-master/dist/sweetalert.css">
    {% load static %}
    <script src="{% static ‘bootstrap-sweetalert-master/dist/sweetalert.js‘ %}"></script>
    <script src="{% static ‘jquery-3.3.1.min.js‘ %}"></script>
    <script src="{% static ‘bootstrap-3.3.7/js/bootstrap.js‘ %}"></script>
</head>
<body>
{# novalidate 屬性是一個布爾屬性。 #}
{# novalidate 屬性規定當提交表單時不對表單數據(輸入)進行驗證。 #}

{# input 的屬性autocomplete 默認為on #}
{# 其含義代表是否讓瀏覽器自動記錄之前輸入的值 #}
{# 很多時候,需要對客戶的資料進行保密,防止瀏覽器軟件或者惡意插件獲取到 #}
{# 可以在input中加入autocomplete="off" 來關閉記錄 #}
{# 系統需要保密的情況下可以使用此參數 #}
<form action="" method="post" novalidate autocomplete="off">
    {% csrf_token %}
    {#    {{ form_obj.as_p }} {# 這裏是自動生成多個p標簽#}
    <p>
        {{ form_obj.user.label }} {# 導入給定的label屬性的值 #}
        {{ form_obj.user }} {# 生成某個字段的input框 #}
        <span style="color: red;">{{ form_obj.user.errors.0 }}</span> {# 當做正常的文本內容來展現 #}
        <span style="color: red;">{{ form_obj.user.errors }}</span> {# 不取0的話會當做一個標簽展現出來 #}
    </p>
    <p>
        {{ form_obj.pwd.label }}
        {{ form_obj.pwd }} {# 生成某個字段的input框 #}
        <span style="color: red">{{ form_obj.pwd.errors.0 }}</span>
        <span style="color: red">{{ form_obj.pwd.errors }}</span>
    </p>
    <button>註冊</button>
</form>
</body>
</html>

常用字段與插件

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

initial

初始值,input框裏面的初始值。

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label=‘用戶名‘,
        initial=‘wjs‘  # 設置默認值
    )
    pwd = forms.CharField(min_length=6, label=‘密碼‘)

error_messages

重寫錯誤信息。

from django import forms


class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label=‘用戶名‘,
        error_messages={
            ‘required‘: ‘不能為空‘,
            ‘invalid‘: ‘格式錯誤‘,
            ‘min_length‘: ‘用戶名最短8位‘
        }
    )
    pwd = forms.CharField(min_length=6, label=‘密碼‘)

password

from django import forms


class LoginForm(forms.Form):
    pwd = forms.CharField(
        min_length=6,
        label=‘密碼‘,
        widget=forms.widgets.PasswordInput(attrs={‘class‘:‘c1‘},
        render_value=True
        )
    )

RadioSelect

單radio值為字符串

from django import forms
from django.forms import widgets


class LoginForm(forms.Form):
    gender = forms.fields.ChoiceField(
        choices=((1, ‘男‘), (2, ‘女‘), (3, ‘不詳‘)),
        label=‘性別‘,
        initial=3,
        widget=forms.widgets.RadioSelect()
    )

# 單選項

單選Select

from django.forms import widgets


class LoginForm(forms.Form):
    hobby = forms.fields.ChoiceField(
        choices=(
            (1, ‘籃球‘),
            (2, ‘足球‘),
            (3, ‘雙色球‘),
        ),
        label=‘愛好‘,
        initial=3,
        widget=forms.widgets.Select()
    )

多選Select

class LoginForm(forms.Form):
    hobby = forms.fields.MultipleChoiceField(
        choices=((1, ‘籃球‘), (2, ‘足球‘), (3, ‘雙色球‘)),
            label=‘愛好‘,
            initial=[1,3],
            widget=forms.widgets.SelectMultiple()
    )

單選checkbox

class LoginForm(forms.Form):
    keep = forms.fields.ChoiceField(
        label=‘是否記住密碼‘,
        initial=‘checked‘,
        widget=forms.widgets.CheckboxInput()
    )

多選checkbox

class LoginForm(forms.Form):
    hobby = forms.fields.MultipleChoiceField(
        choices=((1, ‘籃球‘), (2, ‘足球‘), (3, ‘雙色球‘)),
        label=‘愛好‘,
        initial=[1,3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )  

關於choice的註意事項:

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

方式一:

from django.forms import Form
from django.forms import widgets
from django.forms import fields


class MyForm(Form):
    user = fields.ChoiceField(
        # choices=((1, ‘上海‘), (2, ‘北京‘)),
        initial=2,
        widget=widgets.Select
    )
    
    # 當後臺手動的給數據庫添加數據時,前臺不能的及時獲取到新添加的數據,需要重新啟動服務器,比較麻煩。所以通過調用父類的__init__()方法——刷新頁面時就會獲取到新的數據。
    def __init__(self, *args, **kwargs):
        super(MyForm,self).__init__(*args, **kwargs)
        # self.fields[‘user‘].choices = ((1, ‘上海‘), (2, ‘北京‘)),
        # 或
        self.fields[‘user‘].choices = models.Classes.objects.all().values_list(‘id‘,‘caption‘)

方式二:

from django import forms
from django.forms import fields
from django.forms import models as form_model


class FInfo(forms.Form):
    # 多選
    authors = form_model.ModelMutipleChoiceField(queryset=models.NNewType.objects.all())
    # 單選
    # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())

Django 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類型

Django Form內置字段  

校驗

用自帶驗證規則:

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

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

自定義驗證規則:

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

# 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‘郵箱‘}))

鉤子函數

1. 局部鉤子

def clean_phone(self):
value = self.cleaned_data.get(‘phone‘)
if re.match(r‘^1[3-9]\d{9}$‘,value):
return value

raise ValidationError(‘手機號不正經‘)

2. 全局的鉤子

def clean(self):
pwd = self.cleaned_data.get(‘pwd‘)
re_pwd = self.cleaned_data.get(‘re_pwd‘)

if pwd == re_pwd:
return self.cleaned_data
self.add_error(‘re_pwd‘,‘兩次密碼不一致‘)
raise ValidationError(‘兩次密碼不一致‘)

Django之form表單