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

Django框架 Form组件2(生成标签,验证数据,其他)

程序员文章站 2022-03-07 19:45:01
...

一.生成HTML标签

标签是通过插件生成的

1.通过.<field_name>生成:

#在后端:
<obj>=<form_class>(**<acq_data>):生成<input>
  #参数说明:
    form_class:Form类的名称
    acq_data:默认值构成的dict
      #也可以在定义Form类时给某个字段单独设置默认值
    obj:返回自定义的Form类的实例
      #其中封装了用户提交的数据/错误信息...

#在前端:
{{<obj>.<field_name>}}:生成指定字段的<input>
{{<obj>.<field_name>.<attr>}}:显示指定字段的指定参数的值
  #得到Field类的某个子类的实例对象,但显示在页面上的是<input>
  #参数说明:
    field_name:字段名称
    attr:字段的属性(可取的值参见 Form组件1..2 部分)

2.通过.as_< type>生成:

#在后端:同(1)

#在前端:
{{<obj>.as_<type>}}:生成obj中所有字段构成的指定类型的标签
  #不推荐(定制样式时难以定位)
  #参数说明:
    type:标签类型;可选值见下

#type的可选值:1个字段对应1个该类型的标签
#注意:标签内仍有<input>用于输入,还有其他内容(如label参数)所在的标签
#相当于将1个字段的输入框及所有相关内容包进1个标签
{{<obj>.as_p}}:生成<p>
{{<obj>.as_ul}}:生成<li>(注意外面包上<ul>)
{{<obj>.as_table}}:生成<tr>(注意外面包上<table>)
      ...

二.验证用户输入的数据:
1.内置方法:

<obj>=<form_class>(**<data>):生成实例
<re>=<obj>.is_valid():进行验证
<obj>.cleaned_data:由验证成功的数据构成的dict
  #参数说明:
    form_class:自定义的Form类的名称
    data:用户提交的数据构成的dict
    obj:自定义的Form类的实例
    re:返回验证结果(是否验证成功);bool

<obj>.errors:全部的错误信息
<obj>.errors.<field_name>:指定字段的错误信息
  #参数说明:
    obj:自定义的Form类的实例
    field_name:字段名称

#转换错误信息的数据类型:
<obj>.errors默认的数据类型是ErrorDict
  格式:{["__all__":[<error1>,<error2>...],]#整体的错误信息,来自clean()
        "<field_name1>":[<error1>,<error2>...],
        "<field_name1>":[<error1>,<error2>...],
                      ...
       }
#以下各方法将其转换成其他数据类型:
<obj>.errors.as_ul():默认请求<obj>.errors时会调用该方法
<obj>.errors.as_json()
<obj>.errors.as_data()
<obj>.errors.as_text()

2.自定义验证规则

前2种基于正则表达式的扩展,第3种基于源码的验证流程

(1)通过validators参数:

from django.forms import Form,widgets,fields
from django.core.validators import RegexValidator
 
class MyForm(Form):
    user=fields.CharField(
        validators=[
            RegexValidator(r'^[0-9]+$','请输入数字'),
            RegexValidator(r'^159[0-9]+$','数字必须以1/5/9开头')
            #第1个参数为用于验证的正则表达式,第2个参数为错误提示
        ],
    )
import re
from django.forms import Form,widgets,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):
    phone=fields.CharField(
        #使用自定义验证规则:
        validators=[mobile_validate,],
        error_messages={'required':'手机不能为空'},
        widget=widgets.TextInput(
            attrs={
                'class':"form-control",
                'placeholder':u'手机号码'
            }
        )
    )

(2)通过RegexField:

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

class MyForm(Form):
    user=fields.RegexField(r'^[0-9]+$',error_message={"invalid":"用户名格式错误"})

(3)通过钩子函数:
Django框架 Form组件2(生成标签,验证数据,其他)

对每个字段来说,在进行完基于正则表达式的验证后
将进行基于clean_<field_name>()(1个钩子函数)的验证
  #在1个方法中务必只对1个字段进行验证(因为验证时要对所有字段进行循环,不能确定验证的顺序)
  #相关源码(见上图)属于is_valid()中的self.errors中的self.full_clean()中的self._clean_fields()
  #参数说明:
    field_name:该字段的字段名

#实例:
from django.forms import fields,widgets,Form
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator

class FInfo(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中字段中定义的格式匹配完之后,执行此方法进行验证
    clean_username()的规则为:username中不得包含'666'
    """
        value=self.cleaned_data['username']
        if "666" in value:
            raise ValidationError('666已经被玩烂了...','invalid')
        return value#如果没有设置返回值,该字段在cleaned_data中的值将是None

##########################################################

在full_clean(),执行完self._clean_fields()会执行self._clean_forms()
该函数中会执行self.clean()()(1个钩子函数),钩子本身没有内容,留给用户自定制
  #相关源码(见下图)属于is_valid()中的self._clean_forms()
  #该方法用于同时对多个字段进行验证

#实例:
from django.forms import fields,widgets,Form
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator

class AjaxForm(Form):
    username=fields.CharField()
    user_id=fields.IntegerField(
        widget=widgets.Select(choices=[(0,"a"),(1,"b"),(2,"c")])
    )
    def clean(self):
        value_dict=self.cleaned_data
        u1=value_dict.get("username")
        u2=value_dict.get("user_id")
        if u1=="root" and u2==0:
            raise ValidationError("整体错误信息")
			#这条错误信息会给"__all__"字段
        return self.cleaned_data
        #同样必须返回self.cleaned_data

Django框架 Form组件2(生成标签,验证数据,其他)
Django框架 Form组件2(生成标签,验证数据,其他)

在full_clean()中,执行完self._clean_forms()会执行self._post_clean()(另1个钩子函数)
也可以在该函数中自定制验证规则(甚至不需要返回值),但不常用(该函数的源码见下图)

Django框架 Form组件2(生成标签,验证数据,其他)
(4)同时生成多个标签进行验证:

from django.forms import Form,widgets,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):
    """
    当用户验证都通过后,该值返回给用户
    """
        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]

三.其他
1.ModelForm

在使用Model和Form时,都需要对字段进行定义并指定类型
通过ModelForm则可以省去From中字段的定义

class AdminModelForm(forms.ModelForm):
    class Meta:
        model=models.Admin
        #fields='__all__'
        fields=('username','email')
        widgets={
            'email':forms.PasswordInput(
                attrs={'class':"alex"}
            ),
        }

2.实时更新

使用选择标签时,choices的选项可以从数据库中获取,但由于是静态字段
其值无法实时更新,即在数据库中增加记录后需重启项目后才能获取到
  #说明:静态字段和类绑定,只有在创建类时才会更新,而刷新页面只会导致重新创建实例对象
因此需要自定义构造方法来实现实时更新

(1)方法1(推荐):

通过__init__()来实现

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

class MyForm(Form):
    user=fields.ChoiceField(
        initial=2,
        widget=widgets.Select
    )
    def __init__(self,*args,**kwargs):#创建对象时会执行
        super(MyForm,self).__init__(*args,**kwargs)
        self.fields['user'].widget.choices=models.Classes.objects.value_list('id','caption')
        #self.fields必须在super()下面:因为super()会把所有静态字段拷贝给self.fields
        #如果顺序颠倒,self.fields会找不到静态字段user

(2)方法2:

使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现
  #定制化操作不好,会导致强耦合(需要依赖models.py中对应类中的__str__()),不推荐

from django import forms
from django.forms import fields,widgets,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.UserInfo.objects.all())
    #authors=form_model.ModelChoiceField(queryset=models.UserInfo.objects.all())

#############在mode;s.py中的对应类中补充如下代码#############

def __str__(self):
  return self.username#username是要显示的数据库中的字段

#如果不加__str__()见下图1,如果加__str__()见下图2(仅为效果,非本例实际结果)

Django框架 Form组件2(生成标签,验证数据,其他)
Django框架 Form组件2(生成标签,验证数据,其他)