Django之缓存与信号(十二)
一、缓存
1.1 简介
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django提供了6种缓存方式:
1、开发调试 2、内存 3、文件 4、数据库 5、Memcache缓存(python-memcached模块) 6、Memcache缓存(pylibmc模块)
配置:
通用配置:
'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】)
开发调试:
# 此为开始调试用,实际内部不做任何操作 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎 通用配置 } }
内存:
# 此缓存将内容保存至内存的变量中 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', 通用配置 } } # 注:其他配置同开发调试版本
文件:
# 此缓存将内容保存至文件 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', 通用配置 } } # 注:其他配置同开发调试版本
数据库:
# 此缓存将内容保存至数据库 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', # 数据库表 通用配置 } } # 注:执行创建表命令 python manage.py createcachetable
Memcache缓存(python-memcached模块):
# 此缓存使用python-memcached模块连接memcache CACHES = { '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', ] } }
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', ] } }
1.2 应用
单独视图函数:
settings.py 设置:
# 这里在文件末尾处添加。并新建文件夹 cache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': os.path.join(BASE_DIR,'cache') } }
urls.py 设置:
url(r'^cache/$', views.cache),
views.py 设置:
from django.views.decorators.cache import cache_page @cache_page(10) #如果是@cache_page(60 * 15),即通过装饰器的方式实现,导入模块之后,在需要缓存的函数前加@cache_page(60 * 15) 60*15表示缓存时间是15分钟 def cache(request): import time ctime = time.time() return render(request, 'cache.html', {'ctime': ctime})
cache.html 设置:
<body> <h1>{{ ctime }}</h1> <h1>{{ ctime }}</h1> <h1>{{ ctime }}</h1> </body>
#这样在前端页面在获取的ctime的时候就会被缓存10秒钟,10秒钟之后才会变化,但是这样的话就相当于所有的调用ctime的地方都被缓存了。
局部缓存:
views.py设置:
from django.views.decorators.cache import cache_page # @cache_page(10) #注意这个装饰器是要注释掉的哈 def cache(request): import time ctime = time.time() return render(request, 'cache.html', {'ctime': ctime})
cache.html :
{% load cache %} <!--引入TemplateTag--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <h1>{{ ctime }}</h1> <h1>{{ ctime }}</h1> <!--使用缓存--> {% cache 20 c1 %} <!--cache 缓存时间 缓存key--> <h1>{{ ctime }}</h1> <!--缓存内容--> {% endcache %} </body> </html>
#这样就实现了最后一个ctime缓存,其他两个不缓存,所以多刷两次会看到最后的时间戳小于上面两个的时间戳。
全站缓存:
全站缓存的时候,需要在中间件的最上面添加:
'django.middleware.cache.UpdateCacheMiddleware',
在中间件的最下面添加:
'django.middleware.cache.FetchFromCacheMiddleware',
如下面:
# 使用中间件,经过一系列的认证等操作, # 如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户, # 当返回给用户之前,判断缓存中是否已经存在, # 如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 只有process_response # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', # 只有process_view ] CACHE_MIDDLEWARE_ALIAS = "" CACHE_MIDDLEWARE_SECONDS = "" CACHE_MIDDLEWARE_KEY_PREFIX = ""
#请求时:缓存加在中间件里的最后一个,比如一次经过1、2、3、4中间件,加在4
#返回时:缓存加在中间件里的第一个,如上返回依次经过4、3、2、1,加在1
#其中'django.middleware.cache.UpdateCacheMiddleware'里面只有process_response方法,在'django.middleware.cache.FetchFromCacheMiddleware'中只有process_request方法,所以最开始是直接跳过UpdateCacheMiddleware,然后从第一个到最后一个中间件的resquest,第一次没有缓存座椅匹配urls路由关系依次进过中间件的process_view,到达views函数,再经过process_exception最后经过response,到达FetchFromCacheMiddleware。
二、信号
简介
Django中提供了“信号调度”,用于在框架执行操作时解耦。通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。
2.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 # 创建数据库连接时,自动触发
因为这些信号中并没有注册函数,所以运行时并没有调用触发这些信号
对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:
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代指上面导入的信号,如request_finished,request_started,request_started等,而callback就是你要注册的函数
如果我们把导入信号以及将注册函数都写到一个单独的文件里,为了在程序启动的时候执行信号中的注册函数,可以在于项目同名的文件中的init文件中导入该文件即可
2.2 自定义信号
自定义信号一共需要三步骤:定义信号,注册信号,触发信号
定义信号:
import django.dispatch pizza_done=django.dispatch.Signal(providing_args=["toppings", "size"]) #定义触发信号的时候需要传递两个值进来
注册信号:
def callback(sender, **kwargs): print("callback") print(sender,kwargs) pizza_done.connect(callback)
触发信号:
from 路径 import pizza_done pizza_done.send(sender='seven',toppings=123, size=456)
#由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。
2.3 写个信号小例子
models.py:
from django.db import models # Create your models here. class UserInf(models.Model): user = models.CharField(max_length=32)
python manage.py makemigrations
python manage.py migrate
urls.py :
url(r'^signal/$', views.signal),
主站下面创建一个sg.py:
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 f1(sender, **kwargs): print("xxoo_callback") # print(sender,kwargs) pre_save.connect(f1) import django.dispatch #创建一个信号 pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"]) def callback(sender, **kwargs): #注册一个信号 print("callback") print(sender,kwargs) pizza_done.connect(callback)
views.py:
def signal(reuqest): from app01 import models obj = models.UserInf(user='root') print('end') obj.save() obj = models.UserInf(user='root') obj.save() obj = models.UserInf(user='root') obj.save() from sg import pizza_done #先导入信号 pizza_done.send(sender="asdfasdf",toppings=123, size=456) #pizza_done.send()找到信号名发送,sender="asdfasdf"表示谁发送过来的信号 return HttpResponse('ok')
#上面是后端输出可以看到信号发送完毕并触发了sg.py里面自定义的信号。