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中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。
原文地址:https://www.cnblogs.com/x54256/p/7819564.html