1. 程式人生 > >Django 框架之Form組件

Django 框架之Form組件

all submit ack HA boolean use ogl forms sse

1. Django的Form主要具有以下幾大功能:

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

2. 第一個案例:

# 第一步: 創建Form類
from django.forms import Form
from django.forms import widgets
from django.forms import fields

class MyForm(Form):
    user = fields.CharField(
        widget = widgets.TextInput(attrs={'id':'name', 'class':'cs'})
    )

    gender = fields.ChoiceField(
        choices=((1, '男'), (2, '女'),),
        initial = 2,
        widget = widgets.RadioSelect
    )

    city = fields.CharField(
        initial = 2,
        widget = widgets.Select(choices=((1, '上海'),(2, '北京'),))
    )

    pwd = fields.CharField(
        widget = widgets.PasswordInput(attrs={'class':'cs'}, render_value = True)
    )

# 第二步: View 函數處理
from django.shortcuts import render, redirect
from .forms import MyForm

def index(request):
    if request.method == "GET":
        obj = MyForm()
        return render(request, 'index.html', {'form': obj})
    elif request.method == "POST":
        obj = MyForm(request.POST, request.FILES)
        if obj.is_valid():
            values = obj.clean()
            print(values)
        else:
            errors = obj.errors
            print(errors)
        return render(request, 'index.html', {'form': obj})
    else:
        return redirect('http://www.google.com')

# 第四步: 生成HTML
<form action="/" method="POST" enctype="multipart/form-data">
    <p>{{ form.user }} {{ form.user.errors }}</p>
    <p>{{ form.gender}} {{ form.gender.errors }}</p>
    <p>{{ form.city }} {{ form.city.errors }}</p>
    <p>{{ form.pwd }} {{ form.pwd.errors }}</p>
    <input typy="submit" value="提交"/>
</form>

# PS: 其他標簽
<form method="POST" enctype="multipart/form-data">
    {% csrf_token %}

        {{ form.xxoo.label }}
        {{ form.xxoo.id_for_label }}
        {{ form.xxoo.label_tag }}
        {{ form.xxoo.errors }}
        <p>{{ form.user }} {{ form.user.errors }}</p>
        <input type="submit" value="提交"/>
</form>

3. Form 類

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

3.1 Django 內置字段

Field:
    required=True,              是否允許為空
    widget=None,                HTML 插件
    label=None,                 用於生成Label標簽或顯示內容
    initial=None,               初始值
    help_text='',               幫助信息(在標簽旁邊顯示)
    error_message=None,         錯誤信息 {'require': '不能為空', '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)        格式: 2018-04-02
TimeField(BaseTemporalField)        格式: 11:12
DateTimeField(BaseTemporalField)    格式: 2018-04-02 11:20


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)
    ...         
    queryset,                   查詢數據庫中的數據
    empty_label="=========="    默認空,顯示的內容
    to_field_name=None,         HTML中value的值對應的字段
    limit_choices_to=None       ModelForm中對queryset二次篩選

ModelMultipleChoiceField(ModelChoiceField)
    ...


TypeChoiceField(ChoiceField)
    coerce = lambda val : val   對選中的值進行一次轉換
    empty_value = ''            空值的默認值


MultipleChoiceField(ChoiceField)
    ...

TypeMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val : val   對選中的每一個值進行一次轉換
    empty_value=''              空值的默認值


ComboField(Field)
    fields = ()                 使用多個驗證,

# 示例: 既驗證最大長度20, 又驗證郵箱格式
#   fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])


MultiValueField(Field)
    # 抽象類,子類中可以實現聚合多個字典去匹配一個值, 要配合 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,
                            protocol 必須為both才能啟用


SlugField(CharField)        數字,字母,下劃線, 減號(連字符)
    ...

UUIDField(CharField)        uuid 類型
    ...

3.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

3.2.1 常用選擇插件

# 單radio, 值為字符串
user = fields.CharField(
    initial = 2,
    widget = widgets.RadioSelect(choices=((1, '上海'),(1, '北京'),))
)

# 單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的選項可以從數據庫中獲取,但是由於是靜態字段,獲取的值無法實時更新,
# 那麽需要自定義構造方法,從而達到此目的
# 方式一:
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字段來實現
form 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())

4. 自定義驗證規則

# 方式一:
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開頭')],
    )


# 方式二:
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_message={'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'郵箱'})


# 方式三: 自定義方法
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 中字段中定義的格式匹配完之後,執行此方法進行驗證
        value = self.cleaned_data['username']
        if '999' in value:
            raise ValidationError('999 已經被玩爛了...', 'invalid')
        return value


# 方式四: 同時生成多個標簽進行驗證
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):
        error_messages = {
            'incomplete': 'Enter a country calling code and a phone number.'
        }

        # 各個字段的自定義錯誤信息
        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):
        # 當用戶驗證都通過後,該值返回給用戶
        return data_list


# 自定義插件
class SplitPhoneWidget(widgets.MultiWidget):
    def __init__(self):
        ws = (
            widgets.TextInput(),
            widgets.TextInput(),
            widgets.TextInput(),
        )
        super(SplitPhoneWidget, self).__init__(ws)

    def decompress(self, value):
        # 處理初始值, 當初始值initial不是列表時, 調用該方法
        if value:
            return value.split(',')
        return [None, None, None]

5. 初始化數據

  • 在Web應用程序中,時常用到獲取數據庫中的數據並將值初始化在HTML中的標簽上
# 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()

    city = fields.ChoiceField(
        choices = ((1, '上海'),(2, '北京'),),
        widget = widgets.Select
    )


# Views
from django.shortcuts import render, redirect
from .forms import MyForm

def index(request):
    if request.method == "GET":
        values = {'user': 'root', 'city': 2}
        obj = MyForm(values)

        return render(request, 'index.html', {'form': obj})
    elif request.method == "POST":
        return redirect('http://www.google.com')
    else:
        return redirect('http://www.google.com')


# HTML
<form metho="POST" enctype="multipart/form-data">
    {% csrf_token %}
    <p>{{ form.user }} {{ form.user.errors }}</p>
    <p>{{ form.city }} {{ form.city.errors }}</p>

    <input type="submit" value="提交"/>
</form>


參考資料:

  • Python 全棧
  • Django之Form組件

Django 框架之Form組件