Django框架——cookie与session简介django操作cookie与sessiondjango中间件

Posted XxMa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django框架——cookie与session简介django操作cookie与sessiondjango中间件相关的知识,希望对你有一定的参考价值。

cookie与session简介

"""
回忆:HTTP协议四大特性
	1.基于请求响应
	2.基于TCP、IP作用于应用层之上的协议
	3.无状态
		不保存客户端的状态
	4.无连接
"""
最开始的网站都不需要用户注册 所有人来访问获取到的数据都是一样的
随着互联网的发展很多网站需要指定当前用户的状态

cookie
	保存在客户端与用户状态相关的信息
session
	保存在服务端与用户状态相关的信息
ps:session的工作需要依赖于cookie
    
补充:浏览器有资格拒绝保存服务端发送过来的cookie数据

django操作cookie

from django.shortcuts import render,HttpResponse,redirect
return render()
return HttpResponse()
return redirect()

要想操作cookie就不能直接返回HttpResponse对象 必须先用变量接收
obj1 = render()
return obj1
obj2 = HttpResponse()
return obj2
obj3 = redirect()
return obj3

\'\'\'编写一个真正的用户登录功能\'\'\'
def login_func(request):
    if request.method == \'POST\':
        username = request.POST.get(\'username\')
        password = request.POST.get(\'password\')
        if username == \'jason\' and password == \'123\':
            obj = redirect(\'/home/\')
            obj.set_cookie(\'name\',username)
            return obj
    return render(request,\'loginPage.html\')

### 装饰器
def login_auth(func_name):
    def inner(request,*args,**kwargs):
        if request.COOKIES.get(\'name\'):
        	res = func_name(request,*args,**kwargs)
        	return res
        else:
            return redirect(\'/login/\')
    return inner

@login_auth
def home_func(request):
    return HttpResponse(\'home页面 只有登录的用户才可以查看\')


\'\'\'进阶操作:用户没有登录之前想访问某个网站输入用户名密码之后就应该调回改网站\'\'\'
def login_func(request):
    if request.method == \'POST\':
        username == resquest.POST.get(\'username\')
        password == resquest.POST.get(\'password\')
        if username == \'jason\' and password == \'123\':
            target_path = request.GET.get(\'next\')
            if target_path:
                obj = redirect(target_path)
            else:
                obj = redirect(\'/home/\')
            obj.set_cookie(\'name\',username)
            return obj
    return render(request,\'loginPage.html\')

def login_auth(func_name):
    def inner(request,*args,**kwargs):
        target_path = request.path_info  # 只获取用户输入的路由信息
        if request.COOKIES.get(\'name\'):
        	res = func_name(request,*args,**kwargs)
        	return res
        else:
            return redirect(\'/login/?next=%s\' % target_path)
    return inner

django操作session

由于session是保存在服务端上面的数据 就应该有个地方能够存储
我们只需要执行数据库迁移命令即可 django会自动创建很多需要的表

django默认的session失效时间是14天

设置session
	request.session[\'key\'] = value
    	1.生成一个随机字符串
        2.对value数据做加密处理 并在django_session表中存储
        	随机字符串>>>加密数据
        3.将随机字符串也发送一份给客户端保存(cookie)
        	sessionid:随机字符串
获取session
	request.session.get(\'key\')
    	1.自动获取随机字符串
        2.去django_session表中根据随机字符串获取加密的数据
        3.自动解密数据并处理到request.session.get()中
        
补充说明
	1.可以设置过期时间  # request.session.set_expiry(5)  # 默认是秒为单位
    2.存储session数据的位置也可以修改

\'\'\'代码演示\'\'\'
def set_se_func(request):
    # 会自动转换成字符串的形式存储在表中
    request.session[\'desc\'] = \'时间过的真快 又要干饭了 还是疯狂星期四!!\'
    # 设置过期时间
    request.session.set_expiry(5)  # 默认是秒为单位
    return HttpResponse(\'稍微屯点吃的\')

def get_se_func(request):
    # 存储在表中的数据会自动解密到session.get中
    print(request.session.get(\'desc\'))
    return HttpResponse(\'再坚持一会会\')

django中间件

django默认有七个中间件 并且还支持用户自定义中间件
中间件主要可以用于:网站访问频率的校验 用户权限的校验等全局类型的功能需求
  
MIDDLEWARE = [
    \'django.middleware.security.SecurityMiddleware\',
    \'django.contrib.sessions.middleware.SessionMiddleware\',
    \'django.middleware.common.CommonMiddleware\',
    # \'django.middleware.csrf.CsrfViewMiddleware\',
    \'django.contrib.auth.middleware.AuthenticationMiddleware\',
    \'django.contrib.messages.middleware.MessageMiddleware\',
    \'django.middleware.clickjacking.XFrameOptionsMiddleware\',
]

如何自定义中间件
1. 在项目名下或者应用名下新建一个任意名称的文件夹
2. 在这个文件夹下面新建一个py文件
3. 在这个py文件中,新建一个类,必须继承MiddlewareMixin
4. 在你新建的这个类下面可以写哪几个方法:
	process_reqeust  # 需要传request参数
    process_response  # 需要传request和response参数
    process_view
    process_exception
    process_template_response
5. 一定要在配置文件的中间件里面注册你的中间件路径

from django.utils.deprecation import MiddlewareMixin

class MyMiddleware001(MiddlewareMixin):
    def process_request(self,request):
        print(\'from MyMiddleware001 process_request\')

    def process_response(self,request,response):
        print(\'from MyMiddleware001 process_response\')
        return response
    
在类中编写五个可以自定义的方法
	需要掌握的
    	process_request
        	1.请求来的时候会从上往下一次经过每一个注册了的中间件里面的该方法 如果没有则直接跳过
            2.如果该方法自己返回了HttpResponse对象那么不再往后执行而是直接原路返回
        process_response
        	1.响应走的时候会从下往上依次经过每一个注册了的中间件里面的该方法 如果没有则直接跳过
            2.该方法有两个形参request和response  形参response指代的就是后端想要返回给前端浏览器的数据 该方法必须返回该形参 也可以替换
        \'\'\'如果在执行process_request方法的时候直接返回了HttpResponse对象那么会原路返回执行process_response 不是执行所有的\'\'\'

django中间件三个了解的方法

1.process_view
	路由匹配成功之后执行视图函数/类之前自动触发(顺序同process_request)
2.process_exception
	试图函数/类执行报错自动触发(顺序用process_response)
3.process_template_response
	视图函数/类返回的HttpResponse对象含有render并且对应一个方法的时候自动触发(顺序同process_response)

基于django中间件的功能设计

将各个功能制作成配置文件的字符串形式
	如果想拥有该功能就编写对应的字符串
    如果不想有该功能则注释掉对应的字符串
   
补充知识
	如果利用字符串导入模块
import importlib
s1 = \'bbb.b\'
res = importlib.import_module(s1)  # from bbb import b
print(res)  # <module \'bbb.b\' from \'D:\\\\pythonProject03\\\\djangomiddle\\\\bbb\\\\b.py\'>
\'\'\'注意字符串的结尾最小单位只能是py文件 不能是py文件里面的变量名\'\'\'

需求分析
	模拟编写一个消息通知功能(微信、qq、邮箱)
    
方式1:基于函数封装的版本
	没有眼前一亮的感觉 很一般
方式2:基于django中间件的功能设计(插拔式设计)
#########################################
项目名
	notify
    	__init__.py
        	from settings import NOTIFY_LIST
            import importlib

            def send_all(content):
                for full_path in NOTIFY_LIST:
                    module_path,class_str_name = full_path.rsplit(\'.\',maxsplit=1)
                    # 1.利用字符串导入模块 拿到模块名
                    module_name = importlib.import_module(module_path)
                    # 2.利用反射从模块中获取字符串对应的类名
                    class_name = getattr(module_name,class_str_name)
                    # 3.利用类名加括号产生对象
                    obj = class_name()
                    # 4.对象调用发送消息的方法
                    obj.send_msg(content)
        email.py
        	class Email(object):
    			def __init__(self):
        			pass

    			def send_msg(self,content):
        			print(\'来自email的消息>>>%s\' % content)
        qq.py
        	class Qq(object):
    			def __init__(self):
        			pass

   				def send_msg(self, content):
        			print(\'来自qq的消息>>>%s\' % content)
        weixin.py
        	class Weixin(object):
    			def __init__(self):
        			pass

    			def send_msg(self, content):
        			print(\'来自weixin的消息>>>%s\' % content)
    settings.py
    	NOTIFY_LIST = [
    	\'notify.email.Email\',
    	\'notify.qq.Qq\',
   		\'notify.weixin.Weixin\',
		]
    start.py
    	import notify

		if __name__ == \'__main__\':
    	notify.send_all(\'你酱紫认真!!\')
#########################################

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以上是关于Django框架——cookie与session简介django操作cookie与sessiondjango中间件的主要内容,如果未能解决你的问题,请参考以下文章

django框架中的cookie与session

Django 框架篇: Cookie 与 Session

Django框架--cookie和session

Django框架进阶7 forms组件(pycharm内置测试环境Python Console), cookie与session操作

Django框架(十五)-- cookie和session组件

第十一章 Django框架学习——Cookie和session