zoukankan      html  css  js  c++  java
  • Django框架进阶7 forms组件(pycharm内置测试环境Python Console, 钩子函数), cookie与session操作

    forms组件

    写一个注册页面 获取用户输入的用户名和密码
    用户点击注册发送到后端做用户名密码的校验
    用户名中不能包含金瓶mei     不符合社会主义核心价值观
    密码不能为空            你个DSB,密码怎么能为空

    1.手写获取用户输入的前端页面代码                    渲染页面
    2.后端获取用户数据并做合法性校验                    校验数据
    3.将校验之后的结果渲染到前端页面                    展示信息

    不用forms组件代码:

    app01/views.py

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def register(request):
        error_dic = {'username':'','password':''}
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if '金瓶mei' in username:
                error_dic['username'] = '不符合社会主义核心价值观'
            if not password:
                error_dic['password'] = '密码不能为空'
        return render(request,'register.html',locals())
    View Code

    templates/register.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <form action="" method="post">
        <p>username:
            <input type="text" name="username">
            <span style="color: red">{{ error_dic.username }}</span>
        </p>
        <p>password:
            <input type="text" name="password">
            <span style="color: red">{{ error_dic.password }}</span>
        </p>
        <input type="submit">
    </form>
    </body>
    </html>
    View Code

    pycharm内置测试环境:Python Console

     

    forms组件

    1.渲染页面

    2.校验数据

    3.展示信息

    需要先写一个类

    from django import forms
    
    class MyRegForm(forms.Form):
        # 用户名最少3位  最多8位
        username = forms.CharField(max_length=8,min_length=3)
        password = forms.CharField(max_length=8,min_length=3)
        # email字段必须填写符合邮箱格式的数据
        email = forms.EmailField()

    如何校验数据

    # 1.传入待校验的数据  用自己写的类 传入字典格式的待校验的数据
    form_obj = views.MyRegForm({'username':'jason','password':'12','email':'123456'})
    # 2.判断数据是否符合校验规则
    form_obj.is_valid()  # 该方法只有在所有的数据全部符合校验规则才会返回True
    False
    # 3.如何获取校验之后通过的数据
    form_obj.cleaned_data
    {'username': 'jason'}
    # 4.如何获取校验失败及失败的原因
    form_obj.errors
    {
     'password': ['Ensure this value has at least 3 characters (it has 2).'],
     'email': ['Enter a valid email address.']
     }
    # 5.注意 forms组件默认所有的字段都必须传值 也就意味着传少了是肯定不行的 而传多了则没有任何关系 只校验类里面写的字段 多传的直接忽略了
    form_obj = views.MyRegForm({'username':'jason','password':'123456'})
    form_obj.is_valid()
    Out[12]: False
    form_obj.errors
    Out[18]: {'email': ['This field is required.']}
    
    
    form_obj = views.MyRegForm({'username':'jason','password':'123456',"email":'123@qq.com',"hobby":'hahahaha'})
    form_obj.is_valid()
    Out[14]: True
    form_obj.cleaned_data
    Out[15]: {'username': 'jason', 'password': '123456', 'email': '123@qq.com'}
    form_obj.errors
    Out[16]: {}

    如何渲染页面

    先写一个继承forms的类

    forms组件只帮你渲染获取用户输入(输入 选择 下拉 文件)的标签  不渲染按钮和form表单标签

    渲染出来的每一个input提示信息都是类中字段首字母大写

    {#<p>第一种渲染方式:多个p标签  本地测试方便  封装程度太高了  不便于扩展</p>#}
    {#{{ form_obj.as_p }}#}
    {#{{ form_obj.as_ul }}#}
    {#{{ form_obj.as_table }}#}
    
    {#<p>第二种渲染方式:扩展性较高  书写较为繁琐</p>#}
    {#<label for="{{ form_obj.username.id_for_label }}">{{ form_obj.username.label }}</label>     {# 给label赋id值 #}
    {#{{ form_obj.username }}#}
    {#{{ form_obj.password.label }}{{ form_obj.password }}#}
    {#{{ form_obj.email.label }}{{ form_obj.email }}#}
    
    <p>第三种渲染方式 推荐使用</p>
    {% for form in form_obj %}
        <p>{{ form.label }}{{ form }}</p>
    {% endfor %}

    如何渲染错误信息

    前端

    <form action="" method="post" novalidate>   {# 取消前端校验,前端校验只能显示第一个错误,而且不安全 #}
            {% for form in form_obj %}
                <p>
                 {{ form.label }}{{ form }}      {# 另外注意这个是模板语法 .0索引不会因为对象为空出现超出报错#}
                  <span>{{ form.errors.0 }}</span>  {# 容器类型(此处为列表)会自动被渲染成ul套li形式,加.0固定写法,拿到内部信息 #}
                </p>
        {% endfor %}
        <input type="submit">
    
    </form>

    后端

    def reg(request):
        # 1.先生成一个空的类对象
        form_obj = MyRegForm()
        if request.method == 'POST':
            # 3 获取用户数据并交给forms组件校验  request.POST 字典类型
            form_obj = MyRegForm(request.POST)
            # 4 获取校验结果
            if form_obj.is_valid():
                return HttpResponse('数据没问题')
            else:
                # 5 获取校验失败的字段和提示信息
                print(form_obj.errors)
    
        # 2直接将该对象传给前端页面
        return render(request,'reg.html',locals())

    数据校验一个前后端都得有 但是前端的校验弱不禁风 可有可无
    而后端的校验则必须非常全面

    如何取消浏览器自动帮我们校验的功能
    form表单取消前端浏览器自动校验功能

    <form action="" method="post" novalidate>

    常用参数

    label   input的提示信息
    error_messages    自定义报错的提示信息
    required    设置字段是否允许为空
    initial    设置默认值
    widget    控制type类型及属性

    widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2'})   # form-control占满一行
    widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})

    例:

    class MyRegForm(forms.Form):
        # 用户名最少3位  最多8位
        username = forms.CharField(max_length=8,min_length=3,label='用户名',   #label不写,默认为字段名首字母大写
                                   error_messages={
                                       'max_length':'用户名最长8位',
                                        'min_length':'用户名最短3位',
                                       'required':'用户名不能为空'
                                   },required=False, # 可以不填,为空
                                   initial='jason',  # 默认值
                                   widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2'})
                                   )
        password = forms.CharField(max_length=8,min_length=3,label='密码',
                                   widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}))
        # email字段必须填写符合邮箱格式的数据
        email = forms.EmailField(label='邮箱',error_messages={
            'required':'邮箱必填',
            'invalid':'邮箱格式不正确'
        })

    钩子函数

    全局钩子(针对多个字段)

      校验密码与确认面是否一致

    局部钩子(针对单个字段)

      校验用户名不能包含666

        # 全局钩子  注意如果有错误,页面并不会刷新,输入的数据还在,因为下方reg函数
        def clean(self):    # 钩子函数已被封装,固定名。会在上面MyRegForm输入参数第一层校验通过后,进行第二层校验
            # 校验密码和确认密码是否一致
            password = self.cleaned_data.get('password')
            confirm_password = self.cleaned_data.get('confirm_password')
            if not password == confirm_password:
                # 展示提示信息
                self.add_error('confirm_password','两次密码不一致')
            return self.cleaned_data    # 拿全局数据,要返回全局数据
        # 局部钩子
        def clean_username(self):
            username = self.cleaned_data.get('username')
            if '666' in username:
                self.add_error('username','光喊666是不行的')
            return username     # 拿了局部数据,要返回局部数据

    app01/views.py

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def register(request):
        error_dic = {'username':'','password':''}
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if '金瓶mei' in username:
                error_dic['username'] = '不符合社会主义核心价值观'
            if not password:
                error_dic['password'] = '密码不能为空'
        return render(request,'register.html',locals())
    
    
    from django import forms
    from django.forms import widgets
    from django.core.validators import RegexValidator   # 导入正则表达式
    
    class MyRegForm(forms.Form):
        # 用户名最少3位  最多8位
        username = forms.CharField(max_length=8,min_length=3,label='用户名',   #label不写,默认为字段名首字母大写
                                   error_messages={
                                       'max_length':'用户名最长8位',
                                        'min_length':'用户名最短3位',
                                       'required':'用户名不能为空'
                                   },required=False, # 可以不填,为空
                                   initial='jason',  # 默认值
                                   # widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2'})
                                   )
        password = forms.CharField(max_length=8,min_length=3,label='密码',
                                   # widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})
                                   )
        confirm_password = forms.CharField(max_length=8,min_length=3,label='密码',
                                   # widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})
                                           )
        # email字段必须填写符合邮箱格式的数据
        email = forms.EmailField(label='邮箱',error_messages={
            'required':'邮箱必填',
            'invalid':'邮箱格式不正确'
        })
    
        phone = forms.CharField(
            validators=[
                RegexValidator(r'^[0-9]+$','请输入数字'),
                RegexValidator(r'^159[0-9]+$','数字必须以159开头')
            ]
        )
    
        # 全局钩子  注意如果有错误,页面并不会刷新,输入的数据还在,因为reg函数
        def clean(self):    # 钩子函数已被封装,固定名。会在上面MyRegForm输入参数第一层校验通过后,进行第二层校验
            # 校验密码和确认密码是否一致
            password = self.cleaned_data.get('password')
            confirm_password = self.cleaned_data.get('confirm_password')
            if not password == confirm_password:
                # 展示提示信息
                self.add_error('confirm_password','两次密码不一致')
            return self.cleaned_data    # 拿全局数据,要返回全局数据
    
        # 局部钩子
        def clean_username(self):
            username = self.cleaned_data.get('username')
            if '666' in username:
                self.add_error('username','光喊666是不行的')
            return username     # 拿了局部数据,要返回局部数据
    
    def reg(request):
        # 1.先生成一个空的类对象
        form_obj = MyRegForm()
        if request.method == 'POST':
            # 3 获取用户数据并交给forms组件校验  request.POST
            form_obj = MyRegForm(request.POST)
            # 4 获取校验结果
            if form_obj.is_valid():
                return HttpResponse('数据没问题')
                # return render(request, 'reg.html', locals())
    
            else:
                # 5 获取校验失败的字段和提示信息
                print(form_obj.errors)
    
        # 2直接将该对象传给前端页面
        return render(request,'reg.html',locals())
    View Code

    templates/reg.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    {#<p>第一种渲染方式:多个p标签  本地测试方便  封装程度太高了  不便于扩展</p>#}
    {#{{ form_obj.as_p }}#}
    {#{{ form_obj.as_ul }}#}
    {#{{ form_obj.as_table }}#}
    
    {#<p>第二种渲染方式:扩展性较高  书写较为繁琐</p>#}
    {#<label for="{{ form_obj.username.id_for_label }}">{{ form_obj.username.label }}</label>     {# 给label赋id值 #}
    {#{{ form_obj.username }}#}
    {#{{ form_obj.password.label }}{{ form_obj.password }}#}
    {#{{ form_obj.email.label }}{{ form_obj.email }}#}
    
    <p>第三种渲染方式 推荐使用</p>
    <form action="" method="post" novalidate>   {# 取消前端校验,前端校验只能显示第一个错误,而且不安全 #}
            {% for form in form_obj %}
                <p>
                 {{ form.label }}{{ form }}
                  <span>{{ form.errors.0 }}</span>  {# 容器类型(此处为列表)会自动被渲染成ul套li形式,加.0固定写法,拿到内部信息 #}
                </p>
        {% endfor %}
        <input type="submit">
    
    </form>
    </body>
    </html>
    View Code

    forms补充知识点

    正则校验

    from django import forms
    from django.core.validators import RegexValidator   # 导入正则表达式
    class MyRegForm(forms.Form):
        phone = forms.CharField(
            validators=[
                RegexValidator(r'^[0-9]+$','请输入数字'),
                RegexValidator(r'^159[0-9]+$','数字必须以159开头')
            ]
        )

    渲染选择框,下拉框

    from django import forms
    from django.forms import widgets
    from django.core.validators import RegexValidator
    
    
        ################了解知识点(指定去哪里拷贝即可)################
        gender = forms.ChoiceField(
            choices=((1, ""), (2, ""), (3, "保密")),
            label="性别",
            initial=3,
            widget=forms.widgets.RadioSelect()
        )
    
        hobby = forms.ChoiceField(
            choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
            label="爱好",
            initial=3,
            widget=forms.widgets.Select()
        )
        hobby1 = forms.MultipleChoiceField(
            choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
            label="爱好",
            initial=[1, 3],
            widget=forms.widgets.SelectMultiple()
        )
        keep = forms.ChoiceField(
            label="是否记住密码",
            initial="checked",
            widget=forms.widgets.CheckboxInput()
        )
        hobby2 = forms.MultipleChoiceField(
            choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
            label="爱好",
            initial=[1, 3],
            widget=forms.widgets.CheckboxSelectMultiple()
        )

    django操作cookie与session

    cookie与session的作用
      保存信息

    当你第一次登陆成功之后 服务端给你返回了一个随机字符串
    保存客户端浏览器上 之后再次朝服务端发请求 只需要携带该随机字符串
    服务端就能够识别当前用户身份
    超时时间的概念

    cookie虽然是保存在客户端的浏览器上的 但是是服务端设置的
    浏览器也是可以拒绝服务端对要求 不保存cookie


    cookie
      保存在客户端浏览器上的键值对

    return HttpResponse('...')
    return render(...)
    return redirect(...)
    
    # 变形
    obj = HttpResponse('...')
    return obj
    obj1 = render(...)
    return obj1
    obj2 = redirect(...)
    
    #设置cookie
    obj.set_cookie()  # obj一定要是HttpResponse类直接或间接继承对象
    #获取cookie
    request.COOKIES.get()
    #删除cookie
    obj.delete_cookie()

    获取请求路径

        print('request.path_info:',request.path_info)  # 只拿路径部分 不拿参数
        print('request.get_full_path():',request.get_full_path())  # 路径加参数
            request.path_info: /home/
    
            request.get_full_path(): /home/?username=jason&password=123

    例:

    #装饰器模板
    from functools import wraps  
    
    def login_auth(func):
        @wraps(func)  # 修正装饰器文档功能
        def inner(request,*args,**kwargs):
            print('path_info:',request.path_info)   # 只拿路径部分 不拿参数
            print('get_full_path():',request.get_full_path())   #路径加参数
            # 执行被装饰函数之前你可以做的事情
            target_url = request.path_info
            if request.COOKIES.get('username'):
                res = func(request,*args,**kwargs)
                return res
            else:
                return redirect('/login/?next=%s'%target_url)
        return inner
    
    
    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == 'jason' and password == '123':
                # target_url = request.GET.get('next','/home/') # 直接设置默认值也可以
                target_url = request.GET.get('next')  # 获取地址栏中的信息
                # 判断用户登录之前是否有想要访问的url
                if target_url:
    
                    # 保存用户登录状态
                    obj = redirect(target_url)
                else:
                    obj = redirect('/home/')
                # 设置cookie
                obj.set_cookie('username','jason666',max_age=3)   # 不可设中文  max_age设置cookie3秒后超时
                return obj
    
        return render(request,'login.html')
    
    @login_auth
    def home(request):
        # 校验浏览器是否有对应的cookie
        # if request.COOKIES.get('username'):
        #     print(request.COOKIES)
        #     return HttpResponse('我是home页面 只有登录的用户才能访问')
        # else:
        #     return redirect('/login/')
        return HttpResponse('我是home页面 只有登录的用户才能访问')
    
    @login_auth
    def index(request):
        return HttpResponse('我是index页面 只有登录的用户才能访问')
    
    @login_auth
    def demo(request):
        return HttpResponse('我是demo页面 只有登录的用户才能访问')
    
    @login_auth
    def logout(request):
        obj = HttpResponse('注销了')
        obj.delete_cookie('username')
        return obj

    session
    保存在服务端上的键值对


    设置

    request.session['key'] = value
    """
    1.django内部会自动生成一个随机字符串
    2.去django_session表中存储数据 键就是随机字符串 值是要保存的数据(中间件干的)
    3.将生成好的随机字符串返回给客户端浏览器 浏览器保存键值对
    sessionid 随机字符串
    """

    获取

    request.session.get('key')
    """
    1.django会自动取浏览器的cookie查找sessionid键值对 获取随机字符串
    2.拿着该随机字符串取django_session表中比对数据
    3.如果比对上了 就将随机字符串对应的数据获取出来并封装到request.session供用户调用
    """

    django中默认的session超时时间为14天

    例:

    def set_session(request):
        request.session['username'] = 'jason666'
        return HttpResponse('set_session')
    
    from django.http import JsonResponse
    def get_session(request):
        print(request.session)
        print(request.session.get('username'))
        return HttpResponse('get_session')

     

    # 设置会话Session和Cookie的超时时间
    request.session.set_expiry(value)
    * 如果value是个整数,session会在些秒数后失效。
    * 如果value是个datatime或timedelta,session就会在这个时间后失效。
    * 如果value是0,用户关闭浏览器session就会失效。
    * 如果value是None,session会依赖全局session失效策略。

    例:

    def set_session(request):
        request.session['username'] = 'egonDSB'
        request.session.set_expiry(5)   # session有效期为5秒
        return HttpResponse('set_session')
    
    from django.http import JsonResponse
    def get_session(request):
        print(request.session)
        print(request.session.get('username'))
        return HttpResponse('get_session')
    # 删除当前会话的所有Session数据
    request.session.delete()
      
    # 删除当前的会话数据并删除会话的Cookie。 推荐使用
    request.session.flush() 
    这用于确保前面的会话数据不可以再次被用户的浏览器访问
    例如,django.contrib.auth.logout() 函数中就会调用它。

    例:

    def set_session(request):
        request.session['name'] = 'egon'
        return HttpResponse('set_session')
    
    from django.http import JsonResponse
    def get_session(request):
        print(request.session.get('name'))
        return HttpResponse('get_session')
    
    
    def del_session(request):
        request.session.flush()
        return HttpResponse('注销了')


    session是保存在服务端

    django_session表你还可以把它当成是一个临时的仓库

    注意:

     此处数据是根据浏览器的不同创建的,同一种浏览器只会有一条cookie数据

  • 相关阅读:
    SQL Sever语言 存储过程及触发器
    计蒜客 挑战难题 移除数组中的重复元素
    projecteuler Sum square difference
    码农谷 求前N项之和
    projecteuler Smallest multiple
    计蒜客 挑战难题 寻找插入位置
    Largest palindrome product
    计蒜客 挑战难题 元素移除
    码农谷 球从M米高度自由下落第N次落地时反弹的高度
    挑战难题 奇怪的国家
  • 原文地址:https://www.cnblogs.com/ludingchao/p/12189450.html
Copyright © 2011-2022 走看看