Django之Form组件

Django的form组件的重要功能如下:

  • 生成html标签
  • 验证用户数据(显示错误信息)
  • html form提交时保留上次提交的数据
  • 初始化页面显示内容

一.创建Form类

#!/usr/bin/env python

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": "i1", "class": "c1"}),  # 定制html标签
        label="用户名"
    )

    gender = fields.ChoiceField(
        choices=((1, ""), (2, "")),
        initial=2,  # 设置默认值
        widget=widgets.RadioSelect,
        label="性别"

    )

    city = fields.CharField(
        initial=2,
        widget=widgets.Select(choices=((1,"上海"), (2,"北京"), (3,"重庆"))),
        label="城市"

    )

    pwd = fields.CharField(
        widget=widgets.PasswordInput(attrs={"class":"p1"}, render_value=True),
        label="性别"
    )

2.view函数处理

from django.shortcuts import render, redirect

# Create your views here.
from day60app.form_cls import MyForm


def index(request):
    if request.method == "GET":
        obj = MyForm()
        return render(request, "index.html", {"obj": obj})
    elif request.method == "POST":
        obj = MyForm(request.POST, request.FILES)
        if obj.is_valid():
            v = obj.cleaned_data
            print("用户验证成功信息", v)
            return redirect("http://www.jd.com")
        else:
            v = obj.errors
            print("错误信息", v)
            return render(request, "index.html", {"obj": obj})

3,生成html标签

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Hello World</h1>


<form action="/index/" method="post" enctype="multipart/form-data" novalidate>
    <p>{{ obj.user.label }}{{ obj.user }}{{ obj.user.errors.0 }}</p>
    <p>{{ obj.gender.label }}{{ obj.gender }}{{ obj.gender.errors.0 }}</p>
    <p>{{ obj.pwd.label }}{{ obj.pwd }}{{ obj.pwd.errors.0 }}</p>
    <p>{{ obj.city.label }}{{ obj.city }}{{ obj.city.errors.0 }}</p>

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

Form类

创建Form类时,主要涉及到【字段】和【插件】,字段用于对用户请求数据的验证,插件用于自动生成html

1.Django内置字段如下:

Field

  required = True,   是否允许为空

  widget = None,   HTML插件,默认是TextInput框

  label = None,  用于生成label标签或显示内容

  help_text = '' ,  帮助信息(在标签旁边显示)

  error_messages=None,  错误信息{'required':'不能为空','invalid':'格式错误'}

  show_hidden_initial = False,  是否在单前插件后面在加一个隐藏的且具有默认值的插件(可用于检验两次输入的是否一致,两个input框)

  validators=[],  自定义验证规则

  localize = False,  是否支持本地化

  disabled = False,  是否可编辑

  label_suffix = None,  Label内容后缀

#  示例

class TestForm(forms.Form):
    user = fields.CharField(
        required=True,
        max_length=12,
        min_length=3,
        error_messages={},
        widget = widgets.TextInput(attrs={"class":123}),  # 定制html插件
        # widget= widgets.Textarea()
        label="姓名",
        initial='laiying',
        show_hidden_initial=False,
        # validators=[]  #自定制验证规则
        # disabled=True
        label_suffix=":"


    )

CharField(Field):

  max_length=None,   最大长度

  min_length=None,  最小长度

  strip=True,   是否移除用户输入空白

 

IntegerField(Field)

  max_value=None,   最大值

  min_value=None,  最小值

 

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:00

DateTimeField(BaseTemporalField)  格式: 2016-09-10 11:10

 

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)

all_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=((1,'上海'),(2, '北京'))

  required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
   initial=None,              初始值
    help_text='',              帮助提示
class TestForm(forms.Form):


b = fields.ChoiceField(
        choices=[(1, "电影"), (2, "音乐"), (3, "唱歌")]
    )

 

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

 

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

 

常用选择插件

 1 # 单radio,值为字符串
 2 # user = fields.CharField(
 3 #     initial=2,
 4 #     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
 5 # )
 6  
 7 # 单radio,值为字符串
 8 # user = fields.ChoiceField(
 9 #     choices=((1, '上海'), (2, '北京'),),
10 #     initial=2,
11 #     widget=widgets.RadioSelect
12 # )
13  
14 # 单select,值为字符串
15 # user = fields.CharField(
16 #     initial=2,
17 #     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
18 # )
19  
20 # 单select,值为字符串
21 # user = fields.ChoiceField(
22 #     choices=((1, '上海'), (2, '北京'),),
23 #     initial=2,
24 #     widget=widgets.Select
25 # )
26  
27 # 多选select,值为列表
28 # user = fields.MultipleChoiceField(
29 #     choices=((1,'上海'),(2,'北京'),),
30 #     initial=[1,],
31 #     widget=widgets.SelectMultiple
32 # )
33  
34  
35 # 单checkbox
36 # user = fields.CharField(
37 #     widget=widgets.CheckboxInput()
38 # )
39  
40  
41 # 多选checkbox,值为列表
42 # user = fields.MultipleChoiceField(
43 #     initial=[2, ],
44 #     choices=((1, '上海'), (2, '北京'),),
45 #     widget=widgets.CheckboxSelectMultiple
46 # )

在使用选择标签时,需要注意Choices 的选项可以从数据库中获取,但是由于是静态字段***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。

方式一:推荐方法(*****)

# 特殊的单选或多选时,数据源是否能实时更新

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

class MyForm(Form):
    user = fields.CharField(
    #choices=((1,'小明'),(2,'阿呆'))
        initial=2,
        widget=widgets.Select
    )

    # 自定义构造方法,实时刷新页面数据

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields["user"].widget.choices = models.UserTabel.objects.values_list("id",         "user")

方式二: 

使用django提供的ModelChoiceField和ModelMultipChoiceField字段来实现

 (这种django内置的方法不推荐使用,如果要在前端显示数据库中的用户名,需要依赖models中的__str__方法,而且一个class中只有一个__str__,

如果在有的场景中需要同时显示如id user, age等多个字段名时,这个方法就做不到)

 1 from django.forms import Form
 2 from django.forms import widgets
 3 from django.forms import fields
 4 from django.forms.models import ModelChoiceField
 5 from day60app import models
 6 
 7 
 8 class MyForm(Form):
 9         user = ModelChoiceField(
10         queryset=models.UserTabel.objects.all(),
11         to_field_name="id"
12     )

自定义验证规则

方式一:(基于正则)

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(
    error_message={'invalid':'......'},  #错误提示
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],   # 可用户code 来指定错误的key
    )

方式二:(基于正则)

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'邮箱'}))

方式三:(基于源码流程自定制,对当前字段进行验证)

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.forms.models import ModelChoiceField
from day60app import models
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

class AForm(Form):
        username = fields.CharField()
        user_id = fields.IntegerField(
            widget=widgets.Select(choices=[(1, "赖英"), (2, "春云"), (3, "星星")])
    )

        def clean_username(self):
       """
       Form字段中定义的格式匹配完之后,执行此方法进行验证
       """ v
= self.cleaned_data["username"] if models.UserTabel.objects.filter(user=v).count(): raise ValidationError("用户名已经存在") return self.cleaned_data["username"] def clean_user_id(self): return self.cleaned_data["user_id"] #自定义方法 clean_字段名 #必须返回值self.cleaned_data["username"] #如果错误:raise ValidationError("用户名已存在")

 方法四:(基于源码流程自定制,对多个字段进行验证)

示例一

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.forms.models import ModelChoiceField
from day60app import models

from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
class AForm(Form):
        username = fields.CharField()
        user_id = fields.IntegerField(
            widget=widgets.Select(choices=[(1, "赖英"), (2, "春云"), (3, "星星")])
    )

        def clean_username(self):
            v = self.cleaned_data["username"]
            if models.UserTabel.objects.filter(user=v).count():
               raise ValidationError("用户名已经存在")

            return self.cleaned_data["username"]

        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 == 1:
                raise ValidationError("整体错误信息")
            return self.cleaned_data

示例二

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
 
############## 自定义插件 ##############
class SplitPhoneWidget(widgets.MultiWidget):
    def __init__(self):
        ws = (
            widgets.TextInput(),
            widgets.TextInput(),
            widgets.TextInput(),
        )
        super(SplitPhoneWidget, self).__init__(ws)
 
    def decompress(self, value):
        """
        处理初始值,当初始值initial不是列表时,调用该方法
        :param value:
        :return:
        """
        if value:
            return value.split(',')
        return [None, None, None]

 

posted @ 2017-03-17 17:05  LaniLai  阅读(232)  评论(0)    收藏  举报