美文网首页我爱编程
Django表单(二)

Django表单(二)

作者: Python野路子 | 来源:发表于2018-04-11 20:32 被阅读0次

    什么是django表单

    • django中的表单不是html中的那个表单,这里是指django有一个组件名叫表单
    • 它可以通过配置去验证数据的合法性
    • 同样也可以通过配置生成HTML代码.

    使用表单:

    • 创建一个forms.py的文件,放在指定的app当中,然后在里面写表单。
    • 如果新建了app应用,同样要记得在settings中添加至INSTALLED_APPS。
    • 表单是通过类实现的,继承自`forms.Form,然后在里面定义要验证的字段。
     from django import forms 
    
    • 在表单中,创建字段跟模型是一模一样的,但是没有null=True(是否接受空值NULL,默认值False)或者blank=True(是否接受空白内容,默认为False)等这几种参数了,有的参数是required=True/False(请求能否为空,True不能为空,默认为True)
    class RegisterForm(forms.Form):
        # label 属性是form表单中特有的属性,代表这个字段的描述,这个字典类似于verbose_name
        username = forms.CharField(label = u'用户名',max_length = 20,min_length=3)
        # 存储到数据库的密码,是一个加密后的字符串,但是这里是通过前端传输过来的,并没进行加密
        password = forms.CharField(label= u'密码',max_length=20,min_length=8)
    
    • 表单生成HTML元素:
     # views.py
    class Register(View): #注册功能
        def get(self,request):
            # 如果需要使用django表单渲染html页面
            # 实例化该表单模型,传递给前端
            form = RegisterForm()
            return render(request,'register_form.html',{"form":form})
        def post(self,request):
            # 如果不使用django表单,需要一个一个的值取出来,并且需要自己写对应的验证
            username = request.POST.get('username')
            password = request.POST.get('password')
            return render(request,'register_form.html',locals())
    
     # register.html
    <body>
    {% if username %}
        提交的post数据:
        {{ username }}
        {{ password }}
    {% else %}
    <form action = "{% url "register_form" %}" method="post">
        {% csrf_token %}
        {{ form }}  <!--会自动识别表单属性的 -->
        <input type="submit" vlaue='注册'>
    </form>
    {% endif %}
    </body>
    
    image.png image.png

    ps: 使用django的Form类生成的表单,不包含form和submit按钮两个标签,需要手动添加。
    ps : 这个模块用得比较少,这个功能确实很鸡肋,把前端该做的事情放到后台来实现,增加了代码的耦合性也增加了服务器的压力。

    • 不使用表单生成元素
    1. 写好对应的html元素,HTML表单元素的name必须和django中的表单的name保持一致,否则匹配不到。
    2. 后台post方法调用
     form = RegistForm(request.POST)
    
    • 使用is_valid()方法可以验证用户提交的数据是否合法, 这个方法会返回一个bool,合法返回True, 否则返回False
     form.is_valid()
    
    • is_bound属性:用来表示form是否绑定了数据,如果绑定了,则返回True,否则返回False。即是否为None
    • cleaned_data:这个是在is_valid()返回True的时候,保存用户提交上来的数据。
    #models.py
    from django.db import models
    # Create your models here.
    class User(models.Model):
        username = models.CharField(u'用户名',max_length= 20)
        password = models.CharField(max_length = 40,verbose_name = u'密码')
        class Meta:
            db_table = 'user'
            managed = True
    
    #forms.py
    # -*- coding: utf-8 -*-
    from django import forms
    '''
    forms.py的作用
    它是专门编写你的forms配置的模型
    forms.py本身命名没有要求,你可以为任意名称, 但是我们一般约定它叫forms,代表这个文件是专门处理该APP下处理表单组件的
    '''
    class RegisterForm(forms.Form):
        # label 属性是form表单中特有的属性,代表这个字段的描述,这个字典类似于verbose_name
        username = forms.CharField(label = u'用户名',max_length = 20,min_length=3)
        # 存储到数据库的密码,是一个加密后的字符串,但是这里是通过前端传输过来的,并没进行加密
        password1 = forms.CharField(label= u'密码',max_length=20,min_length=8)
        password2 = forms.CharField(label= u'密码',max_length=20,min_length=8)
    
    #views.py
    class Register(View): #注册功能
        def get(self,request):
            return render(request,'register_form.html')
        def post(self,request):
            # 如果使用表单
            form = RegisterForm(request.POST)
            # is_bound 是一个属性,它只验证数据字段存在不存在,不验证你的数据是否正确
            print(form.is_bound)
            # is_valid,这是一个方法,它会进行所有的验证,包括是否存在,跟数据是否正确
            if form.is_valid():
                # 使用cleaned_data 必须执行完is_valid 且返回为True才能获取数据,保存用户提交上来的数据
                username = form.cleaned_data['username']
                password1 = form.cleaned_data['password']
                password1 = md5_password(password1)
                User.objects.create(username=username,password=password1)
                return HttpResponse('注册成功')
            else:
                return HttpResponse('数据验证失败')
    
    #register_form.html
    <body>
    {% if username %}
        提交的post数据:
        {{ username }}
        {{ password }}
    {% else %}
    <form action = "{% url "register_form" %}" method="post">
        {% csrf_token %}
        用户名:<input type="text" name="username"/><br/>
        密码:<input type="password" name="password1"/><br/>
        确认密码:<input type="password" name="password2"/><br/>
        <input type="submit" vlaue='注册'>
    </form>
    {% endif %}
    </body>
    
    • 上传文件:
      在相应的模型里面定义FileField或者是ImageField类型的字段,并且设置好upload_to参数来指定上传的路径。
    headshot = models.ImageField(u'头像', upload_to="upload/%Y/%m/%d")
    
    • 如果是使用ImageField,会需要安装一个依赖Pillow, Pillow是专门做图片处理的一个python包
     pip install Pillow
    
    • 需要在settings.py文件中指定媒体路径MEDIA_ROOT
     MEDIA_ROOT = "media/"
    

    ps: 这里是媒体文件,它也是一种静态文件,在django中,这一块的内容是要跟其它静态文件分开处理的

    • django 中的文件存储分为两种:
      静态文件存储,一般是我们的JS、css、系统的图片文件等。
      媒体文件存储,一般是用户上传的图片、文件数据,或大的文件或视频等等。
    • 文件上传需要在HTML代码中的form表单中添加enctype="multipart/form-data"以及在views当中,使用request.FILES来接收文件.
     form = LoginUserForm(request.POST, request.FILES)
    
    • 文件只有在保存时才会处理,数据库保存的是文件的路径,不会保存文件本身。
    user = LoginUser(**form.clean())
    user.save()
    
    • 表单错误消息:
    1. 表单验证没有通过后,表单会产生一个errors属性,这个属性包括所有的验证错误信息。
    2. 通过form.errors即可访问。
    3. 通过form['属性名'].erros访问对应的错误
    4. 通过form.errors.get_json_data()form['属性名'].erros.get_json_data()可以将错误消息转换成json数据。
    5. 自定义错误消息:在Field中添加一个error_messagesdict类型的参数,然后根据属性名设置对应的message,例如以下代码:
     password = forms.CharField(max_length=10,error_messages={'required':u'密码不能少'})
    
    1. 必须要执行完is_valid函数,否则errors是不会包含错误
    • 表单自定义错误消息:
      系统自带的表单数据错误信息有时无法满足我们的需求,比如用户是否已经存在了,可以在表单类中自定义错误信息,在表单中,重写方法clean_field(field是一个属性名),可以自定义针对某一个field的验证机制,如果出现错误
      1)如果某个field出现验证错误,通过add_error方法给指定的field添加错误消息。
      2)直接抛出一个raise ValidationError(message, code="属性名")就可以了。
      3)重写clean方法会在先完成django默认的验证后,再重新执行clean方法的验证。
      4)如果验证完成成功了,则直接返回当前值。
     def clean_password(self):
            password = self.cleaned_data.get('password',None)
            if len(password) < 6:
                raise forms.ValidationError(u'password at least 6 length',code='min_length')
            return password
    

    注意:
    1)如果没有设置表单属性,则不会出现相应错误:

    #如果不设置max_length = 20,min_length=3等属性,则不会出现错误;
    password1 = forms.CharField(label= u'密码')
    

    2)设置相关属性:

    password2 = forms.CharField(label= u'密码',max_length=20,min_length=8)
    
    image.png

    3)通过error_messages字典来设置对应message

    username = forms.CharField(label = u'用户名',required= True,max_length = 20,min_length=3,error_messages={'required':u'用户名不能为空','max_length':u'最大长度不能超过20个字符','min_length':u'至少3个字符'})
    
    image.png

    4)自定义错误

        def clean_username(self):
            username = self.cleaned_data['username']
            user = User.objects.filter(username=username)
            if user:
                # 如果这里判断有多个错误存在,则使用add_error方法
                self.add_error('username',u'用户名已存在')
                # 如果只是单个错误,使用raise ValidationError,否则这里raise抛出去了,后面就不能执行了
                # raise forms.ValidationError(u'用户名已存在')
             #敏感字符
            if username.find('mmp') >= 0:
                 self.add_error('username',u'存在敏感字符')
            return username #刚才这里没有返回值  你对username处理完了就没有了没有了  后面的时候views.py、没有数据传进去
    

    不管那种必须要执行完is_valid函数,否则执行相关errors是不会包含错误,form类的运行顺序是init,clean,validte,save;
    如果遇到类似错误,比如说不能为空,最大最小长度时,在error_messages写了错误信息,也自定义了表单错误信息,则required=True时调用error_message,否则自定义的。

    完整参考代码:
    app应用urls.py

    url(r'^register/$', views.Register.as_view(), name="register_form")
    

    models.py:

    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals
    
    from django.db import models
    
    # Create your models here.
    
    class User(models.Model):
        username = models.CharField(u'用户名',max_length= 10)
        password = models.CharField(max_length = 40,verbose_name = u'密码')
        #upload_to的设置会自动在settings配置的media文件夹下创建这样一个文件夹
        #% Y / % m / % d‘被strftime()所格式化;
        # ’ % Y’ 将会被格式化为一个四位数的年份, ‘ % m’ 被格式化为一个两位数的月份’ % d’是两位数日份。
        headshot = models.ImageField(u'头像',upload_to='upload/%Y/%m/%d',null= True)
    
        class Meta:
            db_table = 'user'
            managed = True
    

    表单forms.py:

    # -*- coding: utf-8 -*-
    
    from django import forms
    
    from .models import User
    
    '''
    forms.py的作用
    它是专门编写你的forms配置的模型
    forms.py本身命名没有要求,你可以为任意名称, 但是我们一般约定它叫forms,代表这个文件是专门处理该APP下处理表单组件的
    '''
    
    class RegisterForm(forms.Form):
        # label 属性是form表单中特有的属性,代表这个字段的描述,这个字典类似于verbose_name
        #根据属性名设置对应的message
        username = forms.CharField(label = u'用户名',required= True,min_length = 3,max_length = 10,error_messages={'required':u'用户名不能为空','max_length':u'最大长度不能超过20个字符','min_length':u'至少3个字符'})
        # 存储到数据库的密码,是一个加密后的字符串,但是这里是通过前端传输过来的,并没进行加密
        password1 = forms.CharField(label= u'密码',min_length = 8,max_length = 16)
        #如果没有进行
        password2 = forms.CharField(label= u'密码',min_length = 8,max_length = 16)
        #如果设置required= False则该字段可不填
        headshot = forms.ImageField(label=u'头像',required= False)
    
        #表单自定义错误消息:重写方法clean_field(field是一个属性名),可以自定义针对某一个field的验证机制,一个属性一个对应方法
        def clean_username(self):
            username = self.cleaned_data['username']
            user = User.objects.filter(username=username)
    
            if user:
                # 如果这里判断有多个错误存在,则使用add_error方法
                self.add_error('username',u'用户名已存在')
    
                # 如果只是单个错误,使用raise ValidationError,否则这里raise抛出去了,后面就不能执行了
                # raise forms.ValidationError(u'用户名已存在')
             #敏感字符
            if username.find('mmp') >= 0:
                 self.add_error('username',u'存在敏感字符')
            return username #刚才这里没有返回值  你对username处理完了就没有了没有了  后面的时候views.py、没有数据传进去
    

    视图views.py:

    class Register(View): #注册功能
        def get(self,request):
    
            return render(request,'register_form.html')
    
        def post(self,request):
            #因为我们的文件是通过request.FILES传递的,所以如果需要上传文件,则需要把request.FILES传递进去
            # form = RegisterForm(request.POST)
            form = RegisterForm(request.POST,request.FILES)
            # is_bound 是一个属性,它只验证数据字段存在不存在,不验证你的数据是否正确
            print(form.is_bound)
            # is_valid,这是一个方法,它会进行所有的验证,包括是否存在,跟数据是否正确
            if form.is_valid():
                # 使用cleaned_data 必须执行完is_valid 且返回为True才能获取数据,保存用户提交上来的数据
                username = form.cleaned_data['username']
                password1 = form.cleaned_data['password1']
                password1 = md5_password(password1)
                # User.objects.create(username=username,password=password1,headshot=form.cleaned_data['headshot'])
                #利用字典解包方式
                params = {'username':username,'password':password1,'headshot' : form.cleaned_data['headshot']}
                User.objects.create(**params)
                #如果使用实例化对象方式,一定要记住save保存到数据库中去;
                # user = User(username = username,password=password1,headshot=form.cleaned_data['headshot'])
                # user.save()
    
                return HttpResponse('注册成功')
            else:
                # print(form.errors)
                # print(form['username'].errors)
                # print(form['username'].errors.as_json())
                # print(form.errors.as_json())
    
                return render(request,'register_form.html',locals())
    

    html页面代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>register注册表单页面</title>
        <style>
            * {
                margin: 0;
                padding: 0;
            }
        </style>
    </head>
    <body>
    {% if username %}
        提交的post数据:
        {{ username }}
        {{ password }}
    {% else %}
    <p>用户注册</p>
    {{ form.errors }}
    <!--如果表单需要上传文件,需要设置enctype="multipart/form-data" 否则文件无法找到 -->
    {#{% url 'register_form' %} 通过name找到urls配置的对应视图#}
    <form action = "{% url 'register_form' %}" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        用户名:<input type="text" name="username"/><br/>
        密码:<input type="password" name="password1"/><br/>
        确认密码:<input type="password" name="password2"/><br/>
        上传头像:<input type="file" name = "headshot" /><br/>
        <input type="submit" value="注册"/>
    </form>
    {% endif %}
    </body>
    </html>
    
    学以致用

    需求背景:
    使用表单写一个用户注册
    1)注册需要有头像、用户名、密码(密码存储是加密的)、性别、出生年月
    2)需要做数据验证,字符长度, 是否允许为空等判断
    3)使用自定义异常验证用户名是否存在
    4)使用自定义异常验证两次密码是否相同

    • 分析
    1. 需要数据库创建一个用户表,包含用户名,密码,性别,出生年月,头像字段。--在models.py中需要定义一个用户模型类;
    2. 关于数据验证,对于字符长度,是否允许为空,可以用表单自带的错误消息form.errors前端访问,或者用erros_messages在属性里面设置;
    3. 使用自定义异常验证则用在表单中,重写方法clean_field(field是一个属性名),理清form执行顺序。

    模型models.py:

    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals
    from django.db import models
    # Create your models here.
    class User(models.Model):
        username = models.CharField(u'用户名',max_length= 10)
        password = models.CharField(max_length = 40,verbose_name = u'密码') #存储数据库是用的md5加密后的所以设置长点;
        sex = models.CharField(u'性别',max_length = 4,default='boy',null=True)
        birthday = models.DateField(u'出生日期',auto_now_add=True)
        headshot = models.ImageField(u'头像',upload_to='upload/%Y/%m/%d',null= True)
    
        class Meta:
            db_table = 'user'
            managed = True
    

    表单forms.py:

    # -*- coding: utf-8 -*-
    from django import forms
    from .models import User
    
    class RegisterForm(forms.Form):
        username = forms.CharField(label = u'用户名',required= True,min_length = 3,max_length = 12,error_messages={'required':u'用户名不能为空','max_length':u'最大长度不能超过12个字符','min_length':u'至少3个字符'})
        password = forms.CharField(label= u'密码',min_length = 8,max_length = 16,error_messages={'required':u'密码不能为空','max_length':u'最大长度不能超过16个字符','min_length':u'至少8个字符'})
        password2 = forms.CharField(label= u'密码',min_length = 8,max_length = 16,error_messages={'required':u'密码不能为空','max_length':u'最大长度不能超过16个字符','min_length':u'至少8个字符'})
        sex = forms.CharField(label=u'性别',max_length = 4,required= False)
        birthday = forms.DateField(label=u'出生日期')
        headshot = forms.ImageField(required= False)
    
        def clean_username(self):
            username = self.cleaned_data['username']
            user = User.objects.filter(username=username)
            if user:
                # self.add_error('username',u'用户名已存在')
                raise forms.ValidationError(u'用户名已存在')
            return username
    
        def clean_password2(self):  #不能用clean_password,因为加载这个时候,password2还没加载出来,是没有值的。
            password = self.cleaned_data['password']
            password2 = self.cleaned_data['password2']
            if password != password2:
                raise forms.ValidationError(u'两次密码不一致')
            return password
    

    页面代码:

    <body>
    <h2>用户注册</h2>
    {{ form.errors }}<!--form.errors访问错误信息-->
    <form action = "{% url 'register_form' %}" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        用户名:<input type="text" name="username"/><br/>
        密码:<input type="password" name="password"/><br/>
        确认密码:<input type="password" name="password2"/><br/>
        性别:
            <input type="radio" name="sex">男
            <input type="radio" name="sex">女
        <br/>
        上传头像:<input type="file" name = "headshot" /><br/>
        出生日期:<input type="text" name="birthday"/><br/>
        <input type="submit" value="注册"/>
    </form>
    </body>
    

    应用urls.py:

    # -*- coding:utf-8 -*-
    from django.conf.urls import url,include
    from TestApp import views
    import views
    urlpatterns = [
        url(r'^register/$', views.Register.as_view(), name="register_form")
    ]
    

    视图views.py

    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals
    
    from django.shortcuts import render,redirect,reverse
    from django.views import View
    from django.http import HttpResponse
    import hashlib
    from .models import User
    import uuid
    from .forms import *
    
    # Create your views here.
    def md5_password(password): #使用md5加密
        return hashlib.md5(password).hexdigest()
    class Register(View): #注册功能
        def get(self,request):
            return render(request,'register_form.html')
        def post(self,request):
            form = RegisterForm(request.POST,request.FILES)
            # is_bound 是一个属性,它只验证数据字段存在不存在,不验证你的数据是否正确
            print(form.is_bound)
            # is_valid,这是一个方法,它会进行所有的验证,包括是否存在,跟数据是否正确
            if form.is_valid():
                # 使用cleaned_data 必须执行完is_valid 且返回为True才能获取数据,保存用户提交上来的数据
                username = form.cleaned_data['username']
                password = form.cleaned_data['password']
                password = md5_password(password)
                birthday = form.cleaned_data['birthday']
                print(len(password),password,birthday)
                #利用字典解包方式
                params = {'username':username,'password':password,'birthday':birthday,'headshot' : form.cleaned_data['headshot']}
                User.objects.create(**params)
                #如果使用实例化对象方式,一定要记住save保存到数据库中去;
                # user = User(username = username,password=password1,headshot=form.cleaned_data['headshot'])
                # user.save()
                return HttpResponse('注册成功')
            else:
                return render(request,'register_form.html',locals())
    
    页面效果: image.png
    image.png
    image.png
    image.png
    image.png

    重点

    注意验证流程与逻辑。

    相关文章

      网友评论

        本文标题:Django表单(二)

        本文链接:https://www.haomeiwen.com/subject/khnmhftx.html