Django之Form组件

1.内容回顾

1)Django请求生命周期

HTTP请求->WSGI服务器(WEB服务网关接口)->中间件->url(路由系统)->view(匹配视图函数)->views从model取数据->从前端拿模板->模板渲染给用户返回一个大的字符串(网页内容)    

2)Session是什么?

Session:是保存在服务端的数据,当用户登录时,服务端生成随机字符串给客户端的浏览器,浏览器写到cookie里,服务器端把随机字符串保存起来,服务端的随机字符串和客户端的cookie里的随机字符串一一对应。

3)XSS是什么?

XSS:跨站脚本攻击
        举例:评论(while循环,alert),
        防止XSS(1.客户端提交什么数据,以安全的形式查看,当作字符串让用户访问,2.用户提交时过滤关键字,如script)

4)CSRF-POST(银行的例子) 两个网站

CSRF:跨站请求伪造(POST请求)
        不仅发送数据,还要发送随机字符串(上一次POST请求获取到的)

2.今日内容:

 Form组件

django框架提供了一个Form类,来处理web开发中的表单相关事项。众所周知,Form最常做的是对用户输入的内容进行验证,为此django的forms类提供了全面的内容验证和保留用户上次输入数据的支持。

Form作用有两个1.用户提交数据进行校验;2.保留上次输入内容

提交数据方式:1.Form提交;2.Ajax提交

1. 用户提交数据进行校验

- Form提交(刷新,失去上次内容)
a. LoginForm(Form)
字段名 = xxxx.xxField() # 本质就是验证规则,正则表达式
字段名 = xxxx.xxField() # 本质验证规则,正则表达式
字段名 = xxxx.xxField() # 本质验证规则,正则表达式
字段名 = xxxx.xxField() # 本质验证规则,正则表达式
b. obj = LoginForm(用户提交的数据,request.POST)

c. result = obj.is_valid() 进行校验 print(result)返回True 或者False

d. obj.cleaned_data:是一个字典,如果校验成功了,拿到正确的值

e. obj.errors:是一个对象,包含所有的错误信息

内部原理

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        obj = LoginForm(request.POST)
        #is_valid
        """
        获取当前类所有的对象
        1.LoginForm实例化时,self.fields中
        self.fields={
            'user':正则表达式,
            'pwd':正则表达式,
        }
        2.循环self.fields,
            flag = True验证通过(用户名密码。。都输入正确)
            errors
            cleaned_data
            for k,v in self.fields.items():
                k是user,pwd
                v是:正则表达式
                input_value = request.POST.get(k):前端输入的值,写几个字段,就取谁
            正则表达式和input_value进行正则匹配(这里用的是match)
                flag = False
            return flag
        """
        if obj.is_valid():#如果正确
            print(obj.cleaned_data)#字典类型
            return redirect('http://www.baidu.com')
        else:
            print(obj.errors)
        return render(request, 'login.html', {'obj':obj})
Form提交登录验证代码login函数
def login(request):
                        if request.method == 'GET':
                            return render(request,'login.html')
                        else:
                            obj = LoginForm(request.POST)
                            # is_valid
                            """
                            1. LoginForm实例化时,
                                self.fields={
                                    'user': 正则表达式
                                    'pwd': 正则表达式
                                }
                            2. 循环self.fields
                                  flag = True
                                  errors
                                  cleaned_data
                                  for k,v in self.fields.items():
                                    # 1. user,正则表达式
                                    input_value = request.POST.get(k)
                                    正则表达式和input_value
                                    flag = False
                                  return flag
                           """
                            if obj.is_valid():
                                print(obj.cleaned_data)
                            else:
                                print(obj.errors)
                            return render(request,'login.html')
View Code
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <h1>用户登录</h1>
    <form id="f1" action="/login/" method="POST">
        {% csrf_token %}
        <p>
            用户名:<input type="text" name="user" />{{ obj.errors.user.0 }}
        </p>
        <p>
            密码:<input type="password" name="pwd" />{{ obj.errors.pwd.0 }}
        </p>
        <input type="submit" value="提交">
        <a onclick="submitForm();">提交</a>    #onclick绑定事件
    </form>
    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        function submitForm(){
            $('.c1').remove();    #把以前的错误信息都清除掉
            $.ajax({
                url:'/ajax_login/',    
                type:'POST',        #提交方式
                data:$('#f1').serialize(),    #找到f1标签,jQuery ajax - serialize() 方法,serialize() 方法通过序列化表单值,创建 URL 编码文本字符串。
                datatype:"JSON",                    #serialize()拼接字符串,  user=alex&pwd=456&csrf_token=bing
                success:function(arg){
                    console.log(arg);
                    if (arg.status){

                    }else{
                        $.each(arg.msg,function (index,value) {    循环把所有错误信息输出index,value(也就是key和value )
                            console.log(index,value);
                            var tag = document.createElement('span');    #创建节点:
                            tag.innerHTML = value[0];        #获取第一个错误信息,innerHTML 给节点添加html代码:
                            tag.className = 'c1';
                            $('#f1').find('input[name="'+ index +'"]').after(tag);    #在span标签的后面
                            //'  input[name="  ' + index + '  "]  '==input[name=user]
                        })
                    }
                }
            })
        }
    </script>

</body>
</html>
前端login.html代码

前端涉及知识点:

1) createElement(标签名) :创建一个指定名称的元素。

例:var tag=document.createElement(“input")
tag.setAttribute('type','text');

2)data:$('#f1').serialize()  serialize() 方法可以操作已选取个别表单元素的 jQuery 对象,比如 <input>, <textarea> 以及 <select>。

不过,选择 <form> 标签本身进行序列化一般更容易些:
打包user,pwd,csrf_token,拼接字符串

咱们写的字典也会变成字符串
请求体:'user=alex&pwd=456&csrf_token=bing'

注意:render的本质还是返回HttpResponse(字符串)

return render():
def render(request, template_name, context=None, content_type=None, status=None, using=None):
"""
Returns a HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments.
"""
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)

 

1.obj=Form()form组件类实例化时找到类中所有的字段 把这些字段 变成组合成字典;

self.fields={‘user’:正则表达式1,‘pwd’:正则表达式2}

2.循环self.fields字典(自己写的字段)

for  k,v  in self.fields.items():

    K是user,pwd

   v是正则表达式

3.每次循环通过self.fields字典的键, 去get前端POST提交的数据 得到用户输入数据;

input_value= request.post.get(‘k’)(所以form字段的名称,要和前端的name属性匹配)

4.拿到用户输入的数据 (input_value)和进行正则表达式匹配;

5.匹配成功flag=True 匹配失败flag=falsh,最后 return flag  obj.is_valid=flag。
from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields

class Login(Form):
                              #from验证规则 用户名 6-10字符  required不能为空
    name=fields.CharField(max_length=10,
                          min_length=6,
                          required=True,
                           error_messages={
                               'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                               'min_length':'太短了',
                                'max_length': "太长了",
                                           }

                          )
                                                                    # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
    pwd= fields.CharField(min_length=3,
                          required=True,
                          error_messages={
                              'required': '密码不能为空',  # error_messages参数 自定义错误信息
                              'min_length': '太短了',
                              'max_length': "太长了",
                          }



                          )


def index(request):
    if request.method=='GET':
        return render(request,'login.html')
    else:
        obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一起
        res=obj.is_valid()         #自动校验  给出结果 True 或者 False
        if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
            print(obj.cleaned_data)
            return redirect('http://www.baidu.com')                 #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
        else:
           return  render(request,'login.html',{'obj':obj})
form表单提交验证(form表单(会发起 get)提交刷新失去上次内容)

2.- Ajax提交(不刷新,上次内容自动保留)

PS: Ajax提交(页面不刷新,数据不清空) > Form提交(Form表单一刷新,页面刷新,输入的数据都会清空)    总结:

class Foo(Form):
            字段 = 正则表达式  :是否可以为空,最长,最短
            字段 = 自定义正则表达式
                   1. 常用
                        charField 
  ... 定义正则表达式 参数: 验证:(本质上都是正则表达式) required error_messages 生成HTML标签: widget
=widgets.Select, ******** 用于指定生成怎样的HTML,select,text,input/. label='用户名', # obj.t1.label disabled=False, # 是否可以编辑 label_suffix='--->', # Label内容后缀 initial='666', # 无用,猜测有问题应该在input框中显示默认值 help_text='。。。。。。', # 提供帮助信息

- 生成HTML标签
- 保留上次输入内容

IntegerField继承Field
CharField 继承Field
EmailField继承 CharField
URLField继承CharField

t4 = fields.URLField()
t5 = fields.SlugField() #字母数字下划线,内部也是正则表达式
t6 = fields.GenericIPAddressField()
t7 = fields.DateTimeField()
t8 = fields.DateField()
def ajax_login(request):
    # request.POST.get()
    import json
    ret = {'status':True,'msg':None}
    obj = LoginForm(request.POST)
    if obj.is_valid():
        print(obj.cleaned_data)#{'user': 'alex', 'pwd': 'alexalexalexalexalex'}
    else:
        print(obj.errors)#obj.errors,是一个 对象(无序列表)
        ret['status'] = True
        ret['msg'] = obj.errors
        # error = json.dumps(obj.errors)
        # print(error)
    v = json.dumps(ret)
    print(v)#输入错误:输出{"status": true, "msg": {"user": ["This field is required."], "pwd": ["This field is required."]}}
    #输入正确:{"status": true, "msg": null}
    return HttpResponse(v)#提交错误时,<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li><li>pwd<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
#ajax提交
# {'user': 'alex', 'pwd': 'alexalexalexalexalex'}
    # return render()
AJAX提交方式(ajax_login函数)
    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        function submitForm(){
            $('.c1').remove();
            $.ajax({
                url:'/ajax_login/',
                type:'POST',
                data:$('#f1').serialize(),
                datatype:"JSON",
                success:function(arg){
                    console.log(arg);
                    if (arg.status){

                    }else{
                        $.each(arg.msg,function (index,value) {
                            console.log(index,value);
                            var tag = document.createElement('span');
                            tag.innerHTML = value[0];
                            tag.className = 'c1';
                            $('#f1').find('input[name="'+ index +'"]').after(tag);
                        })
                    }
                }
            })
        }
    </script>
前端jQuery+AJAX代码

Ajax提交验证(不会刷新,上次输入内容自动保留)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajx提交</title>
</head>
<body>
<form method="post" action="/aja_login/" id="f1">
  {%csrf_token%}
<p>用户:<input type="text" name="name"></p>
<p>密码:<input type="password" name="pwd"></p>
<p><input type="button" onclick="Ajxform()" value="aja提交"></p>
</form>
</body>
<script src="/static/zhanggen.js"></script>
<script> function Ajxform(){
    $('.c1').remove()
    $.ajax({
        url:'/alogin/',
        type:'POST',
        dataType:'JSON',
        data:$('#f1').serialize(),
        success:function (args) {
            if (args.status){ }
            else{
{#                {status: false, msg: Object}#}
{#                console.log(args);#}
{#                Jquery循环服务端 传过来的 错误信息对象#}
                $.each(args.msg,function (index,value) {
                    console.log(index,value);
{#                 index----> name ["太短了"]#}
{#                 value-----pwd["密码不能为空"]#}
                    var tag=document.createElement('span');
                    tag.innerHTML= value[0];
                    tag.className='c1';
                    console.log(index);
{#                    寻找input下 属性为 name 和pwd的标签(字符串拼接) 在他们后半加 上tag标签也就是错误 信息 #}
                    $('#f1').find('input[name="'+ index +'"]').after(tag)

                })
            }

               }})}
</script>
</html>
View Code

Views

from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields
import json
class Login(Form):
                              #from验证规则 用户名 6-10字符  required不能为空
    name=fields.CharField(max_length=10,
                          min_length=6,
                          required=True,
                         error_messages={
                               'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                               'min_length':'太短了',
                                'max_length': "太长了",
                                           }

                          )
                                                                    # z注意name 必须和 from表单提交的一致,否则二者怎么对比校验呢
    pwd= fields.CharField(min_length=3,
                          required=True,
                          error_messages={
                              'required': '密码不能为空',  # error_messages参数 自定义错误信息
                              'min_length': '太短了',
                              'max_length': "太长了",})



def agx_login(request):
    ret={'status':True,'msg':None}
    if request.method=='GET':
       return render(request,'ajalogin.html')
    else:
        obj=Login(request.POST)
        ret['status']=False
        ret['msg']=obj.errors
        return HttpResponse(json.dumps(ret))
View Code

 

Form组件之常用字段和参数

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内容后缀
Field

 

动态生成HTML标签,保留用户上次输入的内容。

如何保留用户上次输入的内容?

由于form表单submit之后(发送post请求) 数据提交到 后端,不管前端输入的数据是否正确,服务端也要响应,所以页面会刷新;

所以无法保留用户上次输入的内容;如何解决呢?

 

1、把定义的定义的Form类,实例化(obj=Login() )内部调用一个__str__的方法,如果没有传值 返回<input type="text" name=“字段”>name='字段名空的input标签

2、把这个实例化之后的对象传到前端显示,让用户输入值;用户输入值通过post方法提交到后台。

3、如果后台实例化一个对象 obj=Login(request.POST)传入了值, <input type="text" name=“字段” value='request.post的数据'>然后后端再返回客户端就可以看到用户输入的值了!

from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields
import json
class Login(Form):
                              #Form验证规则 用户名 6-10字符  required不能为空
    name=fields.CharField(max_length=10,
                          min_length=6,
                          required=True,
                         error_messages={
                               'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                               'min_length':'太短了',
                                'max_length': "太长了",
                                           }

                          )
                                                                    # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
    pwd= fields.CharField(min_length=3,
                          required=True,
                          error_messages={
                              'required': '密码不能为空',  # error_messages参数 自定义错误信息
                              'min_length': '太短了',
                              'max_length': "太长了",})


def index(request):
    ret={'status':True,'msg':None}
    if request.method=='GET':
        obj=Login()                 #自动生成空白的input标签 发送给客户端)
        return render(request,'login.html',{'obj':obj})
    else:
        obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一
        res=obj.is_valid()         #自动生成空白的input标签 发送
        if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
            return HttpResponse('OK') #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
        else:
           return render(request,'login.html',{'obj':obj})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<form method="post" action="/login/" id="f1" novalidate >
     {%csrf_token%}
       <h1>用户登录</h1>
        <p>用户名 {{obj.name}}{{ obj.name.errors.0}}</p>
        <p>密码:{{ obj.pwd}}{{ obj.pwd.errors.0}}</p>
        <p><input type="submit" value="登录"></p>
</form>
</body>
</html>
前端页面

 方式一(推荐):

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

老师管理:

老师列表:

class TeacherForm(Form):
    tname = fields.CharField(min_length=2)
    xx = fields.MultipleChoiceField(  #或者ChoiceField
        choices=models.Classes.objects.values_list('id','title'),
        widget=widgets.SelectMultiple
    )
    def __init__(self,*args,**kwargs):
        super(TeacherForm,self).__init__(*args,**kwargs)
        self.fields['xx'].widget.choices = models.Classes.objects.values_list('id','title')
每实例化一次取一次值(也就是每进行一次操作从数据库取一次数据)
obj = TeacherForm() 实例化成对象
1.找到所有字段
2.self.fields={
    tname:fields.CharField(min_length=2)
}
解决TeacherForm类的BUG
def teacher_list(request):
    tea_list = models.Teacher.objects.all()
    print(tea_list)#<QuerySet [<Teacher: Teacher object>, <Teacher: Teacher object>, <Teacher: Teacher object>, <Teacher: Teacher object>]>
    # print(tea_list.tname, tea_list.age, tea_list.title)
    return render(request,'teacher_list.html',{'tea_list':tea_list})
teacher_list
class TeacherForm(Form):
    tname = fields.CharField(min_length=2)
    xx = fields.MultipleChoiceField(  #或者ChoiceField:单选
        choices=models.Classes.objects.values_list('id','title'),
        widget=widgets.SelectMultiple
    )
    def __init__(self,*args,**kwargs):
        super(TeacherForm,self).__init__(*args,**kwargs)
        self.fields['xx'].widget.choices = models.Classes.objects.values_list('id','title')
# obj = TeacherForm()
#1.找到所有字段
#2.self.fields={
#     tname:fields.CharField(min_length=2)
# }
TeacherForm类
def add_teacher(request):
    if request.method == "GET":
        obj = TeacherForm()
        return render(request,'add_teacher.html',{'obj':obj})
    else:
        obj = TeacherForm(request.POST)
        if obj.is_valid():
            # print(obj.cleaned_data)#{'tname': '方少伟', 'xx': ['2', '3']}
            xx = obj.cleaned_data.pop('xx')#把不必要的东西删掉
            row = models.Teacher.objects.create(**obj.cleaned_data)
            print(row)#
            row.c2t.add(*xx)#添加一个字典到第三张表
            return redirect('/teacher_list/')
        # print('okokokokok')
        return render(request,'add_teacher.html',{'obj':obj})
add_teacher

 

修复Bug,刷新无法动态显示数据库内容:
    方式一:
        class TeacherForm(Form):
            tname = fields.CharField(min_length=2)
            # xx = form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all())
            # xx = form_model.ModelChoiceField(queryset=models.Classes.objects.all())

    方式二:
        class TeacherForm(Form):
            tname = fields.CharField(min_length=2)

            xx = fields.MultipleChoiceField(
                widget=widgets.SelectMultiple
            )
            def __init__(self,*args,**kwargs):
                super(TeacherForm,self).__init__(*args,**kwargs)
                self.fields['xx'].widget.choices = models.Classes.objects.values_list('id','title')

 

            Select框:
                单选的方案有两种
                    cls_id = fields.IntegerField(
                        # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')])
                        widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'})
                    )
                    
                    cls_id = fields.ChoiceField(
                        choices=models.Classes.objects.values_list('id','title'),
                        widget=widgets.Select(attrs={'class': 'form-control'})
                    )
                    
                    
                    obj = FooForm({'cls_id':1})
                多选的方案只有一种
                    xx = fields.MultipleChoiceField(
                        choices=models.Classes.objects.values_list('id','title'),
                        widget=widgets.SelectMultiple
                    )

 

方式二:适用于写比较简单的程序,不推荐用这个,必须跟model结合起来用,耦合性太强,依赖数据,依赖__str__方法,程序越写越大,数据库可能会单独拆分出去,有人专门写数据库操作,客户端发HTTP请求,服务端会把结果返回给你,客户端拿到的是JSON对象,那就不是一个models对象了。

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

from django.forms import models as form_model
class TeacherForm(Form):
    tname = fields.CharField(min_length=2) 
  xx=form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all())
定义TeacherForm类

添加老师会出现如下现象:select框全是Classes对象

解决方式:

class Classes(models.Model):
    title = models.CharField(max_length=32)

    def __str__(self):
        return self.title
class Student(models.Model):
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    age = models.IntegerField()
    cls = models.ForeignKey('Classes')

class Teacher(models.Model):
    tname = models.CharField(max_length=32)
    c2t = models.ManyToManyField('Classes')
models.py

在班级类添加一个__str__方法:

    def __str__(self):
        return self.title

然后select框恢复正常:

 Form组件:生成常用标签

class TestForm(Form):
    t1 = fields.CharField(
        widget=widgets.Textarea(attrs={})
    )
    t2 = fields.CharField(
        widget=widgets.CheckboxInput  #
    )
    t3 = fields.MultipleChoiceField(
        choices = [(1,'篮球'),(2,'足球'),[3,'排球']],
        widget=widgets.CheckboxSelectMultiple   #多选
    )
  t4 = fields.MultipleChoiceField(  #这里也可以写成ChoiceField
      choices = [(1,'游泳'),(2,'写代码'),[3,'旅游']],
  widget=widgets.RadioSelect #单选
  )
  
  t5 = fields.FileField(  
  idget=widgets.FileInput
  )
def test(request):
    obj = TestForm()
    return render(request,'test.html',{'obj':obj})

 

  

is_valid-->self.errors错误信息(def errors(self))-->self.full_clean()做验证-->def full_clean(self)-->self._clean_fields(self):
def is_valid(self):
    """
    Returns True if the form has no errors. Otherwise, False. If errors are
    being ignored, returns False.
    """
    return self.is_bound and not self.errors
    初始化时,data(True,要进行验证)   initial(False)  等于True或者False
    
@property
def errors(self):
    "Returns an ErrorDict for the data provided for the form"
    if self._errors is None:
        self.full_clean()  做验证
    return self._errors        
    
  def full_clean(self):
        """
        Cleans all of self.data and populates self._errors and
        self.cleaned_data.
        """
        self._errors = ErrorDict()
        if not self.is_bound:  # Stop further processing.
            return
        self.cleaned_data = {}空字典  字典格式,做校验时填充数据
        # If the form is permitted to be empty, and none of the form data has
        # changed from the initial data, short circuit any validation.
        if self.empty_permitted and not self.has_changed():
            return

        self._clean_fields()  字段
        self._clean_form()
        self._post_clean()
        
    def _clean_fields(self): self.fields是一个字典
        for name, field in self.fields.items():  循环自己的字段
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
            if field.disabled:
                value = self.get_initial_for_field(field, name) 取值:request.POST.get('')
            else: field本质是正则表达式,根据正则表达式和输入的数据进行校验
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)    #根据字段的正则表达式和输入的数据进行校验
                else:
                    value = field.clean(value)  
                self.cleaned_data[name] = value 如果验证通过,self.cleaned_data包含了用户提交过来并且验证成功的值
                if hasattr(self, 'clean_%s' % name):  self代指的就是那个TestForm对象    name就是字段名   clean_字段名
                    value = getattr(self, 'clean_%s' % name)()    #clean开头,name结尾的字段,  加个括号执行函数
                    self.cleaned_data[name] = value
            except ValidationError as e:#抛出异常
                self.add_error(name, e) #加到错误信息
hasattr:判断object中有没有一个name字符串对应的方法或属性,hasattr先判断有没有
getattr(object, name, default=None)  

每一个字段来,先执行正则表达式,再执行函数
如果正则表达式没有通过,函数不会执行

return False
return True

target:提交之后的动作

from django.core.exceptions import ValidationError
class TestForm(Form):
    user = fields.CharField(validators=[])#1
    pwd = fields.CharField()#2

    def clean_user(self):#3
        v = self.cleaned_data['user']
        if models.Student.objects.filter(name=v).count(): #如果验证数据成功了
            # raise ValueError('用户名已经存在')
            raise ValidationError('用户名已经存在',code='invalid')    #code='invalid' / 'required' 'max_length' 'min_length'
        return self.cleaned_data['user']#必须有返回值,否则返回None
    def clean_pwd(self):#4
        return self.cleaned_data['pwd']
    def clean(self):
        user = self.cleaned_data.get('user')
        email = self.cleaned_data.get('email')
        if models.Student.objects.filter(user=user,email=email).count(): #表示已经用户名已经存在
            raise self.cleaned_data #
        return self.cleaned_data
        
def _clean_form(self):
    try:
        cleaned_data = self.clean() #如果返回None则不会重新赋值
    except ValidationError as e:#如果出错执行该语句
        self.add_error(None, e)  
    else:
        if cleaned_data is not None: #如果没出错执行该语句
            self.cleaned_data = cleaned_data  #重新赋值
    def clean(self): #这个功能可以自定制
        """
        Hook for doing any extra form-wide cleaning after Field.clean() has been
        called on every field. Any ValidationError raised by this method will
        not be associated with a particular field; it will have a special-case
        association with the field named '__all__'.
        """  Hook:钩子
        return self.cleaned_data
    def _post_clean(self):
        """
        An internal hook for performing additional cleaning after form cleaning
        is complete. Used for model validation in model forms.
        """
        pass
# 执行顺序:1-3-2-4
Form验证源码分析
c. 扩展
    - is_valid
        - 字段 = 默认正则表达式
             - 额外的正则
                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开头')],
                    )
        - clean_字段,必须返回值
        - clean()  
            有返回值:cleaned_data = 返回值
            无返回值:cleaned_data = 原来的值
扩展知识
1. 使用
    class Foo:
        xx = xxxxxx() # 正则,插件
        
        
        def clean_xx():
            ..
            
            
        def clean():
            pass
            
2. 页面展示
    obj = Foo()
    obj = Foo(init..)
    
    # 灵活
        <form>
            {{obj.xx}}
            {{obj.xx}}
            {{obj.xx}}
            {{obj.xx}}
        </form>
    # 简单
        {{obj.as_p}}
        <ul>
            {{obj.as_ul}}
        </ul>
        <table>
            {{obj.as_table}}
        </table>

3. 后台
    is_valid()
    clean_data
    errors    可以被JSON序列化
思考题:
class Input:
def init(self,attrs,value):
    self.attrs

def __str__():
    return <input values='x'  />
Form总结

文件上传功能

Form文件上传:

from django.core.files.uploadedfile import InMemoryUploadedFile
def f1(request):
    if request.method == "GET":
        return render(request,'f1.html')
    else:
        # print(request.FILES)#输出对象<MultiValueDict: {}>   #<MultiValueDict: {'fafafa': [<InMemoryUploadedFile: QQ图片20170210015937.gif (image/gif)>]}>
        # print(request.FILES.get('fafafa'))#输出对象 拿到字符串,而getlist拿到的是列表,文件内容  QQ图片20170210015937.gif
        # print(request.FILES.getlist('fafafa'))#输出对象InMemoryUploadedFile [<InMemoryUploadedFile: QQ图片20170210015937.gif (image/gif)>]
        file_obj = request.FILES.get('fafafa')
        # print(type(file_obj))#<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
        # print(file_obj.name)
        # print(file_obj.size)
        # f = open(file_obj.name,'wb')
        f = open(os.path.join('static',file_obj.name),'wb') 
        for chunk in file_obj.chunks():
            f.write(chunk)
        f.close()
        return render(request,'f1.html')
View Code
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <form method="POST" action="/f1/" enctype="multipart/form-data">
        {% csrf_token %}
        <input type="file" name="fafafa">
        <input type="submit" value="提交">
    </form>

</body>
</html>
前端页面

注意事项:

<form method="POST" action="/f2/" enctype="multipart/form-data">
    {% csrf_token %}

    {{ obj.user }}
    {{ obj.fafafa }}

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

form表单栏必须加这一句:enctype="multipart/form-data",否则上传不成功
View Code

 

  

 

posted on 2017-07-05 00:20  bigdata_devops  阅读(234)  评论(0编辑  收藏  举报

导航