• Django——session,csrf,中间件,信号,缓存


    session

    使用session前一定要先python manage.py makemigration+python manage.py migrate

    基于cookie做用户验证的时候,敏感的信息不适合放在cookie中,所以引出session

    a.session原理

    • cookie是保存在用户浏览器端的键值对
    • session是保存在服务器端的键值对,session是基于cookie做的

    session相当于内存中的一个大字典

    session = {
    	随机字符串1 : {'username':'alex1','pwd':'123',...}
    	随机字符串2 : {'username':'alex2','pwd':'1234',...}
    	...
    }
    
    1.生成随机字符串
    2.写到用户浏览器的cookie中
    3.保存到session中
    4.在对应字典中设置相关内容(私密信息)
    

    实际上Django就是做的以上的操作

    设置session

    获取session

     1 def login(request):
     2     if request.method == 'GET':
     3         return render(request,'login.html')
     4 
     5     elif request.method == 'POST':
     6         u = request.POST.get('user')
     7         p = request.POST.get('pwd')
     8         if u == 'root' and p == '123':
     9             request.session['username'] = u
    10             request.session['is_login'] = True
    11             if request.POST.get('rmb',None) == '1':
    12                 print(123)
    13                 request.session.set_expiry(2)
    14 
    15             return redirect('/index/')
    16         else:
    17             return redirect("/login/")
    18 
    19 def index(request):
    20     if request.session['is_login']:
    21         return HttpResponse('OK')
    22     else:
    23         return HttpResponse('')
    设置+获取session

    b.session的使用

    # 获取Session中数据
    request.session['k1']
    request.session.get('k1',None) ***
    
    # 设置Session中数据
    request.session['k1'] = 123
    request.session.setdefault('k1',123) # 存在则不设置
    
    # 删除Session中数据
    del request.session['k1']	#只删除k1内的
    request.session.clear()		#清除session中所有的数据 
    
    # 所有 键、值、键值对
    request.session.keys()
    request.session.values()
    request.session.items()
    request.session.iterkeys()	#返回一个迭代器,不可以print,数据量大的时候for循环效率高
    request.session.itervalues()
    request.session.iteritems()
    
    ********** 知道就行 **********
    
    # 用户session的随机字符串
    request.session.session_key
    
    # 将所有Session失效日期小于当前日期的数据删除
    request.session.clear_expired()
    
    # 检查 用户session的随机字符串 在数据库中是否
    request.session.exists("session_key")
    
    # 删除当前用户的所有Session数据
    request.session.delete("session_key")
    
    
    ********** 重点 **********
    
    # 设置服务器+浏览器端的超时时间
    # 可以加个判断条件对某一个人设置超时时间
    
    request.session.set_expiry(value)
        * 如果value是个整数,session会在些秒数后失效。
        * 如果value是个datatime或timedelta,session就会在这个时间后失效。
        * 如果value是0,用户关闭浏览器session就会失效。
        * 如果value是None,session会依赖全局session失效策略。
    

    示例

     1 views.py
     2 
     3 def login(request):
     4     if request.method == 'GET':
     5         return render(request,'login.html')
     6 
     7     elif request.method == 'POST':
     8         u = request.POST.get('user')
     9         p = request.POST.get('pwd')
    10         if u == 'root' and p == '123':
    11             request.session['username'] = u
    12             request.session['is_login'] = True
    13             if request.POST.get('rmb',None) == '1':
    14                 print(123)
    15                 request.session.set_expiry(2)   #将服务器和浏览器的超时时间都设置成2s
    16 
    17             return redirect('/index/')
    18         else:
    19             return redirect("/login/")
    20 
    21 def index(request):
    22     if request.session.get('is_login',None):
    23         return HttpResponse('OK')
    24     else:
    25         return HttpResponse('')
    26 
    27 
    28 login.html
    29 
    30 <!DOCTYPE html>
    31 <html lang="en">
    32 <head>
    33     <meta charset="UTF-8">
    34     <title>Title</title>
    35 </head>
    36 <body>
    37     <form action="/login/" method="post">
    38         <input type="text" name="user" />
    39         <input type="text" name="pwd" />
    40         <input type="checkbox" name="rmb" value="1"/>2s免登陆
    41         <input type="submit" value="提交" />
    42         <input id='btn1' type="button" value="ajax" />
    43     </form>
    44 
    45 </body>
    46 </html>
    View Code

    c.配置(settings)文件中设置默认操作(通用配置):

    SESSION_COOKIE_NAME = "sessionid"           
    # Session的cookie保存在浏览器上时的key(辣个传说中的随机字符串),即:sessionid=随机字符串(默认)
    
    SESSION_COOKIE_PATH = "/"                   
    # Session的cookie保存的路径(默认 就看你是存在数据库、缓存...视情况而定)
    
    SESSION_COOKIE_DOMAIN = None                 
    # Session的cookie保存的域名(默认)
    
    SESSION_COOKIE_SECURE = False                
    # 是否Https传输cookie(默认不是https传输)
    
    SESSION_COOKIE_HTTPONLY = True               
    # 是否Session的cookie只支持http传输(默认是http传输)
    
    SESSION_COOKIE_AGE = 1209600                 
    # Session的cookie失效日期(默认超时时间是两周)
    
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False      
    # 是否关闭浏览器使得Session过期(如果为True就是不帮我们写超时时间)(默认)
    
    SESSION_SAVE_EVERY_REQUEST = False           
    # 是否每次请求都保存Session,默认修改之后才保存(默认设置10s,10s就退出,改为True就按最后一次操作时间计时)
    

     d.引擎的配置

    a.数据库Session(默认)
    	SESSION_ENGINESESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
    	# Django默认是将Session数据存储在数据库中,即:django_session 表中。
    
    b.缓存Session
    	SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
        SESSION_CACHE_ALIAS = 'default'     # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
    
    	配置memcache:
    	CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.dummy.DummyCache',     # 引擎
                'TIMEOUT': 300,                                               # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
                'OPTIONS':{
                    'MAX_ENTRIES': 300,                                       # 最大缓存个数(默认300)
                    'CULL_FREQUENCY': 3,                                      # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
                },
                'KEY_PREFIX': '',                                             # 缓存key的前缀(默认空)
                'VERSION': 1,                                                 # 缓存key的版本(默认1)
                'KEY_FUNCTION' 函数名                                          # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
            }
        }
    	
    c.文件Session
    	SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
        SESSION_FILE_PATH = None        # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir
    
    d.缓存+数据库Session
    	SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
     
    e.加密cookie Session(我觉得和盐加密cookie没啥区别)
    	SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
    

    CSRF

    a.简介

      django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。

    全局:
      中间件 django.middleware.csrf.CsrfViewMiddleware
    
    局部:
    	from django.views.decorators.csrf import csrf_exempt,csrf_protect
    
    	@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
    	@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
    

    b.form表单提交 / ajax提交

     1 login.html
     2 
     3 <!DOCTYPE html>
     4 <html lang="en">
     5 <head>
     6     <meta charset="UTF-8">
     7     <title>Title</title>
     8 </head>
     9 <body>
    10     <form action="/login/" method="post">
    11         {% csrf_token %}
    12         <input type="text" name="user" />
    13         <input type="text" name="pwd" />
    14         <input type="checkbox" name="rmb" value="1"/>2s免登陆
    15         <input type="submit" value="提交" />
    16         <input id='btn1' type="button" value="ajax" />
    17     </form>
    18 
    19     <script src="/static/jquery-1.12.4.js"></script>
    20     <script src="/static/jquery.cookie.js"></script>
    21     <script>
    22 
    23         $.ajaxSetup({
    24             beforeSend:function (xhr,settings) {
    25                 xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken'));
    26             }
    27         });
    28 
    29         $('#btn1').click(function () {
    30             $.ajax({
    31                 url:'/login/',
    32                 type:'POST',
    33                 data:{'user':'root','pwd':'123'},
    34 {#                headers:{'X-CSRFtoken':$.cookie('csrftoken')},#}
    35                 sucess:function (data) {
    36 
    37                 }
    38             })
    39         })
    40 
    41     </script>
    42 </body>
    43 </html>
    44 
    45 views
    46 
    47 def login(request):
    48     if request.method == 'GET':
    49         return render(request,'login.html')
    50 
    51     elif request.method == 'POST':
    52         u = request.POST.get('user')
    53         p = request.POST.get('pwd')
    54         if u == 'root' and p == '123':
    55             request.session['username'] = u
    56             request.session['is_login'] = True
    57             if request.POST.get('rmb',None) == '1':
    58                 print(123)
    59                 request.session.set_expiry(2)   #将服务器和浏览器的超时时间都设置成2s
    60 
    61             return redirect('/index/')
    62         else:
    63             return redirect("/login/")
    64 
    65 def index(request):
    66     if request.session.get('is_login',None):
    67         return HttpResponse('OK')
    68     else:
    69         return HttpResponse('')
    View Code

    中间件(请求的生命周期加上中间件)

    django 中的中间件(middleware),在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。

    在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件,如下图。

    与mange.py在同一目录下的文件夹 wupeiqi/middleware下的auth.py文件中的Authentication类  #这就是自定义的中间件

    中间件中可以定义四个方法,分别是:

    • process_request(self,request)
    • process_view(self, request, callback, callback_args, callback_kwargs)
    • process_template_response(self,request,response)  #一般用不到
    • process_exception(self, request, exception)
    • process_response(self, request, response)  

    以上方法的返回值可以是None和HttpResonse对象,如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户。

    就不继续向下执行了

     

    拦截请求只需要return HttpResponse

    自定义中间件

    1、创建中间件类

    2、注册中间件(加入配置文件)

    >>运行输出结果,证明了上图的走的路径

     1 from django.utils.deprecation import MiddlewareMixin
     2 
     3 
     4 class Row1(MiddlewareMixin):
     5     def process_request(self,request):
     6         print('a')
     7 
     8     def process_view(self, request, view_func, view_func_args, view_func_kwargs):
     9         print('b')
    10 
    11     def process_response(self, request, response):
    12         print('c')
    13         return response
    14 
    15 class Row2(MiddlewareMixin):
    16     def process_request(self,request):
    17         print('1')
    18 
    19     def process_view(self, request, view_func, view_func_args, view_func_kwargs):
    20         print('2')
    21 
    22     def process_response(self, request, response):
    23         print('3')
    24         return response
    25 
    26 
    27 class Row3(MiddlewareMixin):
    28     def process_request(self,request):
    29         print('aa')
    30 
    31     def process_view(self, request, view_func, view_func_args, view_func_kwargs):
    32         print('bb')
    33 
    34     def process_response(self, request, response):
    35         print('cc')
    36         return response
    View Code

    process_exception用法

    1、执行完所有 request 方法 
    
    2、执行 所有 process_view方法
    
    3、如果视图函数出错,执行process_exception(最终response,process_exception的return值)
    
    如果process_exception 方法有了 返回值 就不再执行 其他中间件的 process_exception,直接执行response方法响应 
    
    4.执行所有response方法
    
    5.最后返回process_exception的返回值 

    process_template_response用法

    1、默认不执行
    
    2、只有在视图函数的返回对象中有render方法才会执行!
    
    3、并把对象的render方法的返回值返回给用户(注意不返回视图函数的return的结果了,而是返回视图函数 return值(对象)的render方法)
    

    中间件应用场景

    由于中间件工作在 视图函数执行前、执行后(像不像所有视图函数的装饰器!)适合所有的请求/一部分请求做批量处理

    1、做IP限制

    放在 中间件类的列表中,阻止某些IP访问了;

    2、URL访问过滤

    如果用户访问的是login视图(放过)

    如果访问其他视图(需要检测是不是有session已经有了放行,没有返回login),这样就省得在 多个视图函数上写装饰器了!

    3、缓存(还记得CDN吗?)

    客户端请求来了,中间件去缓存看看有没有数据,有直接返回给用户,没有再去逻辑层 执行视图函数

    缓存

    a.开发调试

    # 此为开始调试用,实际内部不做任何操作
        # 配置:
            CACHES = {
                'default': {
                    'BACKEND': 'django.core.cache.backends.dummy.DummyCache',     # 引擎
                    'TIMEOUT': 300,             # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
                    'OPTIONS':{
                        'MAX_ENTRIES': 300,     # 最大缓存个数(默认300)
                        'CULL_FREQUENCY': 3,    # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3,也就是默认一次剔除100)
                    },
                    'KEY_PREFIX': '',           # 缓存key的前缀(默认空)
                    'VERSION': 1,               # 缓存key的版本(默认1)
                    'KEY_FUNCTION' 函数名       # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
                }
            }
    
    ************************* 下面的无论什么配置都一样 *************************
        # 自定义key
        def default_key_func(key, key_prefix, version):
            """
            Default function to generate keys.
    
            Constructs the key used by all other methods. By default it prepends
            the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
            function with custom key making behavior.
            """
            return '%s:%s:%s' % (key_prefix, version, key)	#上面的值传来组成-> key= :1:函数名
    
        def get_key_func(key_func):
            """
            Function to decide which key function to use.
    
            Defaults to ``default_key_func``.
            """
            if key_func is not None:
                if callable(key_func):
                    return key_func
                else:
                    return import_string(key_func)
            return default_key_func
    

    b.内存

    # 此缓存将内容保存至内存的变量中
        # 配置:
            CACHES = {
                'default': {
                    'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
                    'LOCATION': 'unique-snowflake',
                }
            }
    
        # 注:其他配置同开发调试版本

     c.文件

    # 此缓存将内容保存至文件
        # 配置:
    
            CACHES = {
                'default': {
                    'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
                    'LOCATION': '/var/tmp/django_cache',
                }
            }
        # 注:其他配置同开发调试版本
    

    d.数据库

    # 此缓存将内容保存至数据库
    
        # 配置:
            CACHES = {
                'default': {
                    'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
                    'LOCATION': 'my_cache_table', # 数据库表
                }
            }
    
        # 注:执行创建表命令 python manage.py createcachetable
    

    e、Memcache缓存(python-memcached模块)以后回来看

    # 此缓存使用python-memcached模块连接memcache
    
        CACHES = {    #连接memcache
            'default': {
                'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
                'LOCATION': '127.0.0.1:11211',
            }
        }
    
        CACHES = {    #连接本地的文件
            'default': {
                'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
                'LOCATION': 'unix:/tmp/memcached.sock',
            }
        }   
    
        CACHES = {    #连接集群(做一个简单的分布式分布到两台机器)
            'default': {
                'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
                'LOCATION': [
                    '172.19.26.240:11211',
                    '172.19.26.242:11211',
                ]
            }
        }
        CACHES = {    #连接集群,分比重
            'default': {
                'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
                'LOCATION': [
                    ('172.19.26.240:11211',10),
                    ('172.19.26.242:11211',11),
                ]
            }
        }
    

    f、Memcache缓存(pylibmc模块)

    # 此缓存使用pylibmc模块连接memcache
        
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
                'LOCATION': '127.0.0.1:11211',
            }
        }
    
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
                'LOCATION': '/tmp/memcached.sock',
            }
        }   
    
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
                'LOCATION': [
                    '172.19.26.240:11211',
                    '172.19.26.242:11211',
                ]
            }
        }
    

    缓存的应用

    a. 全站使用
    使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存
    
        MIDDLEWARE = [
            'django.middleware.cache.UpdateCacheMiddleware',
            # 其他中间件...
            'django.middleware.cache.FetchFromCacheMiddleware',
        ]
    
        CACHE_MIDDLEWARE_ALIAS = ""		#多长时间刷新一下
        CACHE_MIDDLEWARE_SECONDS = ""
        CACHE_MIDDLEWARE_KEY_PREFIX = ""
    	
    b.单独视图缓存
    方式一:在函数上加装饰器
        from django.views.decorators.cache import cache_page
    
        @cache_page(60 * 15)
        def my_view(request):
            ...
    
    方式二:缓存一个url
        from django.views.decorators.cache import cache_page
    
        urlpatterns = [
            url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
        ]
    
    c、局部视图使用
    1、引入TemplateTag
    
            {% load cache %}
    
    2、使用缓存
    
        {% cache 5000 缓存key %}
            缓存内容
        {% endcache %}	
    

    如果三个都有的话,最外层的生效(想想请求生命周期就知道了)

    信号 

    Django提供一种信号机制,一些动作发生时,会触发信号,然后监听了这个信号的函数就会被执行。比如,实现数据库每写入一条数据,写一条日志。要实现这个需求,可以通过全局的中间件来做,但是利用Django的信号机制会更灵活。中间件只作用在请求进来和响应出去时,而信号的散布范围更广。

    1、Django内置信号

    Model signals
        pre_init                    # django的modal执行其构造方法前,自动触发
        post_init                   # django的modal执行其构造方法后,自动触发
        pre_save                    # django的modal对象保存前,自动触发
        post_save                   # django的modal对象保存后,自动触发
        pre_delete                  # django的modal对象删除前,自动触发
        post_delete                 # django的modal对象删除后,自动触发
        m2m_changed                 # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
        class_prepared              # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
    Management signals
        pre_migrate                 # 执行migrate命令前,自动触发
        post_migrate                # 执行migrate命令后,自动触发
    Request/response signals
        request_started             # 请求到来前,自动触发
        request_finished            # 请求结束后,自动触发
        got_request_exception       # 请求异常后,自动触发
    Test signals
        setting_changed             # 使用test测试修改配置文件时,自动触发
        template_rendered           # 使用test测试渲染模板时,自动触发
    Database Wrappers
        connection_created          # 创建数据库连接时,自动触发
    

    2、信号的使用

    from django.core.signals import request_finished
        from django.core.signals import request_started
        from django.core.signals import got_request_exception
    
        from django.db.models.signals import class_prepared
        from django.db.models.signals import pre_init, post_init
        from django.db.models.signals import pre_save, post_save
        from django.db.models.signals import pre_delete, post_delete
        from django.db.models.signals import m2m_changed
        from django.db.models.signals import pre_migrate, post_migrate
    
        from django.test.signals import setting_changed
        from django.test.signals import template_rendered
    
        from django.db.backends.signals import connection_created
    
    
        def callback(sender, **kwargs):    #定义要做什么
            print("xxoo_callback")
            print(sender,kwargs)
    
        xxoo.connect(callback)
        # xxoo指上述导入的内容
    
        pre_init.connect(callback)    #在__init__触发前触发我们注入的函数
    

    然后要将该文件引入到与工程同名的目录下的__init__文件引入该函数

    3、自定义信号

    a. 定义信号
    
    import django.dispatch
    pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])  #[]里面是触发要传的2个参数
    #信号的名字
    b. 注册信号 def callback(sender, **kwargs): print("callback") print(sender,kwargs) pizza_done.connect(callback) c. 触发信号 from 路径 import pizza_done pizza_done.send(sender='seven',toppings=123, size=456) 由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。
  • 相关阅读:
    专升本高等数学
    Clash教程
    LeetCode Weekly Contest 289
    LeetCode No6 Z字形变换
    LeetCode No10 正则表达式匹配
    LeetCode No9 回文数
    LeetCode No7 整数反转
    LeetCode No8 字符串转换整数 (atoi)
    LeetCode No11 盛最多水的容器
    11、第3次用蛋糕预拌粉
  • 原文地址:https://www.cnblogs.com/x54256/p/7819564.html
Copyright © 2020-2023  润新知