欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

django-Form组件

程序员文章站 2022-04-25 15:18:09
...

Widgets

每个表单字段都有一个对应的Widget 类,它对应一个HTML 表单Widget,例如。

在大部分情况下,字段都具有一个合理的默认Widget。例如,默认情况下,CharField 具有一个TextInput Widget,它在HTML 中生成一个。

表单渲染

对于

{{ form.as_table }} 以表格的形式将它们渲染在<tr> 标签中
{{ form.as_p }} 将它们渲染在<p> 标签中
{{ form.as_ul }} 将它们渲染在<li> 标签中

注意,你必须自己提供

    元素。

{{ form.as_p }}会渲染如下:

Django内置字段

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类型
    ...

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
# )

实例代码

views.py

from django.shortcuts import render, redirect, HttpResponse

from django import forms
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
from django.forms import widgets
from app07 import models


class RegistForm(forms.Form):
    user =  forms.CharField(max_length=18, min_length=3,
                           error_messages={
                               'required': '不能为空',
                               'min_length': 'too short',
                               'max_length': "too long"
                           })


    pwd = forms.CharField(max_length=32, min_length=3,
                          error_messages={
                              'max_length': "too long",
                              'required': '不能为空',
                              'min_length': 'too short',
                          },
                          widget=widgets.PasswordInput(attrs={"class":"active"}))

    rePwd = forms.CharField(label='确认密码',
                                 max_length=32,
                                 error_messages={},
                                 widget=widgets.PasswordInput(attrs={"class":"active"}))


    email = forms.EmailField(
        error_messages={
            "invalid": '格式错误'
        }
    )


    phone =forms.CharField()

    def clean_user(self):
        val = self.cleaned_data.get("user")
        ret = models.User.objects.filter(name=val)
        if not ret:
            return val
        else:
            raise ValidationError("该用户已经注册")

    def clean_phone(self):
        val = self.cleaned_data.get('phone')
        import re
        ret = re.search("1[356789]\d{9}$", val)
        if ret:
            return val
        else:
            raise ValidationError("手机号格式错误")

    def clean(self):
        pwd = self.cleaned_data.get('pwd')
        repeat_pwd = self.cleaned_data.get('rePwd')
        if pwd == repeat_pwd:
            return self.cleaned_data
        else:
            raise ValidationError("两次密码不一致")



def regist(request):
    if request.method == "POST":
        registForm = RegistForm(request.POST)
        # 全部校验无误
        if registForm.is_valid():
            print("is_valid cleaned_data-------",registForm.cleaned_data)
            user = registForm.cleaned_data.get("user")
            pwd = registForm.cleaned_data.get("pwd")

            models.User.objects.create(name=user,pwd=pwd)

            return HttpResponse("ok")
        else:
        # 最少存在一个字段错误
            print("errors-------",registForm.errors) #  {"字段":["","",""]}
            # print("errors-------",registForm.errors["pwd"])  # {"pwd":""}
            print("is_not_valid clean_data",registForm.cleaned_data) #{'user': 'safly'}
            print(registForm.errors.get("__all__"))
            return render(request,"regist.html",{"registForm": registForm})



    registForm = RegistForm()

    return render(request, "regist.html", {"registForm": registForm})

regist.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>


    <style>

        .active{
            border: 1px solid crimson;

        }

        .err_span{
            color: red;
        }
    </style>
</head>
<body>


<form action="" method="post">

    {% csrf_token %}
    {{ registForm.as_p }}

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


<hr>

<form action="/regist/" method="post" novalidate>
    {% csrf_token %}

    <div>

        <label for="">输入姓名:</label> {{ registForm.user }}
        <span class="err_span">{{ registForm.errors.user.0 }}</span>

    </div>

    <div>
        <label for="">输入密码:</label>  {{ registForm.pwd }}
        <span class="err_span">{{ registForm.errors.pwd.0 }}</span>

    </div>

     <div>
        <label for="">{{ registForm.rePwd.label }}</label>  {{ registForm.rePwd }}
        <span class="err_span">{{ registForm.non_field_errors.0}}</span>

    </div>


     <div>
        <label for="">输入邮箱:</label>  {{ registForm.email }}
        <span class="err_span">{{ registForm.errors.email.0 }}</span>

    </div>

     <div>
        <label for="">输入电话:</label>  {{ registForm.phone }}
        <span class="err_span">{{ registForm.errors.phone.0 }}</span>

    </div>

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


</body>
</html>

如果校验全队输出如下:

is_valid cleaned_data------- {'user': 'dfdsfsdf', 'pwd': 'rrrr', 'rePwd': 'rrrr', 'email': 'aaa@qq.com', 'phone': '13545678908'}

如果我们按着如下的校验:
局部钩子只校验 用户名、手机
全局钩子只校验密码
如果我们邮箱输错、电话输错,密码输错,看下最后输出是什么?
django-Form组件

errors------- <ul class="errorlist"><li>phone<ul class="errorlist"><li>手机号格式错误</li></ul></li><li>__all__<ul class="errorlist nonfield"><li>两次密码不一致</li></ul></li></ul>

is_not_valid clean_data {'user': '梵蒂冈', 'pwd': 'rrrr', 'rePwd': 'fdsf', 'email': 'aaa@qq.com'}

<ul class="errorlist nonfield"><li>两次密码不一致</li></ul>

errors里面只是密码框、手机号框输出,因为局部钩子没有校验邮箱
clean_data 用户名、密码、确认密码、邮箱

django-Form组件

django-Form组件

以下是伪代码,我们forms组件渲染有3种方式,我们还可以利用如下的方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <style>
        span {
            color: red;
        }
    </style>
</head>
<body>
<h3>注册页面</h3>
<form action="" novalidate method="post">
    {% csrf_token %}
    {% for field in reg_form %}
        <div>
            <label for="">{{ field.label }}</label>
            {{ field }}<span>{{ field.errors.0 }}</span>
            {% if field.label == '确认密码' %}
                <span>{{ all_error.0 }}</span>
            {% endif %}
        </div>
    {% endfor %}
    <input type="submit">
</form>
</body>
<html>