Form组件-细节及补充

校验顺序: 1自己内部的校验规则——2局部钩子(按照字段的顺序来)——3全局钩子

视图函数中执行结果的细节

from django.shortcuts import render,HttpResponse
##引入校验的类
from app01.my_forms import UserForm

#视图函数
def my_forms(request):
    if request.method == 'GET':
        form = UserForm()
        return render(request, 'index.html',{'form':form})

    if request.method == 'POST':
        # print(request.POST)
        """
        ############### 一、#################
        # 测验
        f = UserForm({'name': 'whww', 'email': '123','xxx':'xxx'})
        # print(f.is_valid())#返回布尔值,只有全校验成功才返回true
        if f.is_valid():
            #如果全部交验成功,以字典形式返回键值对
            print(f.cleaned_data) #{'name': 'whww', 'email': '123@qq.com'}
        else:
            #如果校验不成功,表示有不符合要求的字段
            #校验成功的字段
            print(f.cleaned_data)#{'name': 'whww'}
            # print(type(f.cleaned_data)) #<class 'dict'>
            #校验不成功的字段:格式——{'name':['.........']}
            print(f.errors)#<ul class="errorlist"><li>email<ul class="errorlist"><li>Enter a valid email address.</li></ul></li></ul>
            # print(type(f.errors)) #<class 'django.forms.utils.ErrorDict'>
            print(f.errors.get('email')) #<ul class="errorlist"><li>Enter a valid email address.</li></ul>
        """
        """
          ################# 二、##############
          (1) if 所有字段校验成功,则f.cleaned_data, {'name': 'whww', 'email': '123@qq.com'}
          (2) 如果前面两个字段校验成功,那么后面的'xxx'就不校验了,也不管他,返回name与email校验的结果;
              如果前面校验的字段不够,比如没有email,只有name跟xxx,会返回false     
          (3) if没有校验成功:
              正确的放在 f.cleaned_data ——>{'name':'whw',...} ——字典类型
              错误的放在 f.errors ——>{'email':[............]} ——自定义的字典类型 jango.forms.utils.ErrorDict 
        """
        ##校验用户输入的信息
        #form表单的name属性值应当与forms组件的字段名称一致
        form = UserForm(request.POST)
        print(form.is_valid())
        if form.is_valid():
            print(form.cleaned_data)
            #只有全部验证成功了才执行
            return HttpResponse('OK!')
        else:
            print(form.errors)
            #全局钩子错误
            ##获取全局钩子错误
            full_errors = form.errors.get('__all__')
            #带着参数返回当前页面
            return render(request,'index.html',locals())

生成Html标签的三种方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Forms组件</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css">
    <link rel="stylesheet" href="/static/whw_css.css">
</head>
<body>
<h3>注册页面</h3>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-lg-offset-3">
        
            {# 方式一  直接手写不用渲染 name属性的值要跟校验的form类的属性值一致#}
            <form action="" method="post">
                {% csrf_token %}
                <p>用户名: <input type="text" name="name"></p>
                <p>密码: <input type="text" name="pwd"></p>
                <p>确认密码: <input type="text" name="r_pwd"></p>
                <p>邮箱: <input type="text" name="email"></p>
                <p>手机号: <input type="text" name="tel"></p>
                <input type="submit" value="提交">
            </form>

             {# 方式二  forms组件的渲染方式一 这种方式相对自由~比如我想给用户名这里单独加一个“这个用户已经注册”的提示 可以用这种方式 #} 
             ###注意这个novalidate 阻止浏览器自带的弹窗校验
            <form action="" method="post" novalidate >
                {% csrf_token %}

                <p>用户名 {{ form.name }}<span class="pull-right">{{ form.name.errors.0 }}</span></p>
                <p>密码 {{ form.pwd }}<span class="pull-right">{{ form.pwd.errors.0 }}</span></p>
                <p>确认密码 {{ form.r_pwd }}<span class="pull-right">{{ form.r_pwd.errors.0 }}</span><span class="pull-right">{{ full_errors.0 }}</span></p>
                <p>邮箱 {{ form.email }}<span class="pull-right">{{ form.email.errors.0 }}</span></p>
                <p>手机号 {{ form.tel }}<span class="pull-right">{{ form.tel.errors.0 }}</span></p>
                <input type="submit" value="确认">
            </form>

             {# 方式三  forms组件的渲染方式二 for循环批量添加~但是这种方式没办法为单独的标签添加额外的校验  #}
            <form action="" method="post">
                {% csrf_token %}

                {% for f in form %}
                    <p>  {# 注意~这里for可以放id~打一个id pycharm会自动提示对应标签的id #}
                        <label for="">{{ f.label }}</label>
                        {{ f }}
                    </p>
                {% endfor %}

                <input type="submit" value="确认">
            </form>
        </div>
    </div>
</div>

</body>
</html> 

视图函数中的操作-获取校验数据、批量写入数据库

def index(request):
    if request.method == 'GET':
        #Get请求时实例化一个MyForm对象并用它去前端生成对应的标签
        form_obj = MyForm()
        return render(request, 'index.html', {'form_obj': form_obj})
    elif request.method == 'POST':
        post_data= request.POST
        # print(post_data)
        # 用POST中的值实例化一个MyForm对象
        form_obj = MyForm(post_data)
        if form_obj.is_valid(): #验证每个字段传过来的数据是不是正确的
            #cleaned_data方法
            data = form_obj.cleaned_data
            #cleaned_data获取到的是一个是一个字典
            print(data)
            #先把authors pop出来~因为书跟作者是多对多关系~需要单独处理,而且得到的author_data是一个QuerySet对象,里面装着选中的author对象们
            author_data = data.pop('authors')
            print(author_data)
            #除了多对多关系 其他字段统一加入数据库中~将字典打散
            book_obj = models.Book.objects.create(**data)
            #多对多关系 单独处理
            book_obj.authors.add(*author_data)
            return HttpResponse('ok')
        else:
            # 校验不成功的话~带着错误信息与之前的数据再渲染前端页面~这样用户之前的输入的数据不会丢失而且能显示错误信息
            return render(request, 'index.html', {'form_obj': form_obj})

关于正则校验

import re
# 引入正则相关抛出的异常
from django.core.validators import RegexValidator from django.core.exceptions import ValidationError #正则校验1 写一个函数 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 Myform(forms.Form): title = forms.Charfield( label='书名', max_length=16, min_length=12, initial = 'xx', help_text = 'aaaa', required=False, error_messages={'max_length':'xxxx'.....}, widget = forms.widgets.TextInput(attrs={'type':'date'}), widget = forms.widgets.PasswordInput(attrs={'class':'...'},render_value=True), #正则校验1 在里面调用 validators = [RegexValidator(r'^a','错误信息'),mobile_validate,]
    '''
#正则校验2 或者可以直接在内部写简单的正则~但是要用RegexValidator方法抛出异常! validators=[RegexValidator(r'^a','必须以a开头'),],
    '''
) price
=forms.IntergerField...

关于__init__方法

在自定义的MyForm类中重写init方法,主要实现两个功能~批量添加样式常用~关于choices看下面总结的

class MyForm(forms.Form):
    ...
    
     #(1)指定choices值 注意必须有后面两个动态参数
     def __init__(self,*args,**kwargs):
# 别忘了执行父类的init方法 super().
__init__(*args,**kwargs) # 单选下拉框与单选radio self.fields['city'].choices = models.City.objects.all().values_list('pk','name') #[(1,'包头') #(2)批量添加样式 def __init__(self,*args,**kwargs):
     # 别忘了执行一下父类的init方法 super().
__init__(*args,**kwargs) for field in self.fields: self.fields[field].widget.attrs.update({ 'class': 'form-control' })

关于选择的下拉框

写死的方式

单选框

sex = forms.ChoiceField(
        choices=(
            ('1',''),
            ('2',''),
        ),
        # 注意 Select是原生的~加样式要在Select中加
        # widget=forms.widgets.RadioSelect()  #RadioSelect是拓展的~不可以加样式
        widget=forms.widgets.Select(attrs={'class': 'form-control'})

    )

复选框

authors = forms.MultipleChoiceField(
        choices=(
            ('1', 'whw'),
            ('2', 'naruto'),
            ('3','sasuke'),
        ),
        widget=forms.widgets.CheckboxSelectMultiple(),
    )

从数据库中提取数据—常用

单选框

publish = forms.ModelChoiceField(
        # 必须是queryset这个关键字参数,从数据库中获取到所有的数据
        queryset=models.Publish.objects.all(),
        # widget=forms.widgets.Select(attrs={'class': 'form-control'})
        widget=forms.widgets.Select()
    )

复选框

authors = forms.ModelMultipleChoiceField(
        queryset=models.Author.objects.all(),
        # widget = forms.widgets.SelectMultiple(attrs={'class':   'form-control'})
        widget = forms.widgets.SelectMultiple()
    ) 

关于单选的checkbox——七天内自动登陆

单选的checkbox常用在登陆页面中~提示用户是否7天内自动登陆~

class TestForm2(forms.Form):
        # keep生成一个复选框标签
        keep = forms.ChoiceField(
           # 注意 这里choices里的第一个值是True与False~后台只识别这两个!
            choices=(
                ('True',1),
                ('False',0),
            ),
            label="是否7天内自动登录",
            # 默认值是1 被选中
            initial="1",
            # 样式
            widget=forms.widgets.CheckboxInput(), 
        )
单选的checkbox:
只提供两个值 ~字符串形式的True与False~~choices中元组的第一个值只能是这两个!后面的值可以是任意的 form只是帮我们做校验~校验选择内容的时候~就是看在没在我们的choices里面~里面有这个值表示合法~没有会在前端提示错误!

选中:'True'~form只是帮我们做校验,校验选择内容的时候,就是看在没在我们的choices里面,里面有这个值,表示合法,没有就不合法。其实就是看True与False。

没选中:'False'

---保存到数据库里面  keep:'True'

后台判断:

if keep == 'True':
        session 设置有效期7天
    else:
        pass

局部钩子与全局钩子

局部钩子

——错误的话抛出异常;正确的话记得return val!

#局部钩子
    #用户名是否已经存在/不能为纯数字
    def clean_name(self):
        val = self.cleaned_data.get('name') #cleaned_data是一个有序字典
        # 数据库中检测
        ret = models.UserInfo.objects.filter(name=val)
        if ret:
            raise ValidationError('该用户名已经注册了!')
        elif val.isdigit():
            raise ValidationError('用户名不能全为数字')
        else:
            return val
    #手机号必须为11位
    def clean_phone(self):
        val = self.cleaned_data.get('phone')
        if len(val) == 11:
            return val
        else:
            raise ValidationError('手机号必须为11位')
    #邮箱已经注册过了
    def clean_email(self):
        val = self.cleaned_data.get('email')
        # 数据库中检测
        ret = models.UserInfo.objects.filter(email=val)
        if ret:
            raise ValidationError('这个邮箱已经注册过了!')
        else:
            return val

全局钩子

 # 全局钩子
    def clean(self):
        p1 = self.cleaned_data.get('password')
        p2 = self.cleaned_data.get('r_password')
        if p1 == p2:
            # 将cleaned_data全部返回
            return self.cleaned_data
        else:
#将全局钩子的错误显示在某个字段的错误里 self.add_error(
'r_password','两次输入的密码不一致') # 需要注意全局钩子的错误在form_obj对象中~局部获取不到! # raise ValidationError('两次输入的密码不一致!') 这样写的话局部取不到

局部钩子的校验顺序问题

局部钩子中其实也可以校验其他的字段,但是要注意执行顺序!

像上面这样的定义的顺序~可以在birthday的局部钩子中的cleaned_data拿到password与r_password通过校验的值~在这里可以进行密码与确认密码的校验(当然在r_password的cleaned_data中当然也可以~只是为了说明问题)~~
但是,在name的局部钩子不能拿到password与r_password通过校验的值!因为name在两者之前!校验name的时候还没有他们两个校验后的值!

password字段输入错误后保留输入数据

 # 针对password~输错的话会清空输入框~在widget设置render_value=True可以不清空
    password = forms.CharField(max_length=20, label='密码',
                               widget=widgets.PasswordInput(render_value=True,attrs={'class': 'form-control', 'placeholder': '密码'}),
                               error_messages={'required': '输入不能为空', },
                               )

all

posted on 2019-06-07 09:51  江湖乄夜雨  阅读(150)  评论(0编辑  收藏  举报