缓存
为什么要使用缓存?
用户的每次请求如果都从数据库中获取数据模板渲染,将降低性能,增加服务器压力,而且客户端无法即时获得响应。如果将渲染后的结果放到缓存中,每次请求都会检查缓存中是否有对应的资源,如果有就会从缓存中获取数据。不过缓存都会有自己的生效时间,来保证数据的更新性。
配置
内存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) } } }
可以在settings.py中单独填写CACHE_MIDDLEWARE_SECONDS = 数字, 来设置缓存超时时间,优先级高于CACHES中的TIMEOUT
文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': 'D:cache', 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) } } }
数据库
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', # 数据库表 } # 执行创建表命令 python manage.py createcachetable }
数据库是socket通信,其速度不如文件快,而且数据库所获得的数据需要进行渲染,而文件中是已经渲染过的字符串,因此速度要快于数据库
Memcache缓存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) } } }
应用
全站应用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', ] CACHE_MIDDLEWARE_ALIAS = "" CACHE_MIDDLEWARE_SECONDS = "" CACHE_MIDDLEWARE_KEY_PREFIX = ""
全站缓存是在中间件中进行操作,请求进来时,通过FetchFromCacheMiddleware获取缓存资源,响应结果出去时,如果缓存中不存在请求起源,则通过django.middleware.cache.UpdateCacheMiddleware来将响应内容以字符串的形式缓存下来。
全站缓存适用于实时性要求不高的网站,比如个人博客
单独试图
方式一:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
方式一: from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request): ... 方式二: from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), ]
方式二:在路由中进行配置
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^test.html/$', cache_page(5)(views.test)), # 将cache_page(5) 加到视图前面 ]
局部模板
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
a. 引入TemplateTag {% load cache %} b. 使用缓存 {% cache 5000 缓存key %} 缓存内容 {% endcache %} 示例: #HTML: {% load cache %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% cache 5 'asda' %} <h1>{{ date }}</h1> {% endcache %} </body> </html> #View: def index(request): date = time.time() return render(request,"index.html",{"date":date})
信号
内置信号
from django.db.models.signals import pre_save
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 # 创建数据库连接时,自动触发
示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
项目目录下的同名目录下的__init__.py中写入,因为这个文件在项目运行时会自动运行 from django.db.models.signals import pre_save def callback1(sender,**kwargs): print("callback1",sender,kwargs) pre_save.connect(callback1) View: def users(request): user_obj=models.UserInfo(name='xiaobai',pwd='123',ut_id=1) user_obj.save() return HttpResponse("OK") 打印结果: callback1 <class 'app01.models.UserInfo'> {'signal': <django.db.models.signals.ModelSignal object at 0x0000029CCE6828D0>, 'instance': <UserInfo: UserInfo object>, 'raw': False, 'using': 'default', 'update_fields': None}
自定义信号
新建模块命名为mysignal,写入: import django.dispatch pizza_done=django.dispatch.Signal(providing_args=["toppings","size"]) def callback(sender,**kwargs): print("xiaobai_callback",sender,kwargs) pizza_done.connect(callback) View: def users(request): user_obj=models.UserInfo(name='xiaobai2',pwd='123',ut_id=1) user_obj.save() from mysignal import pizza_done pizza_done.send(sender='xiaobai',toppings=request,size=666) return HttpResponse("OK") 打印结果: xiaobai_callback xiaobai {'signal': <django.dispatch.dispatcher.Signal object at 0x000001F9F094B860>, 'toppings': <WSGIRequest: GET '/users.html'>, 'size': 666}
性能相关
示例: models: class UserInfo(models.Model): name=models.CharField(max_length=32) pwd=models.CharField(max_length=32) ut=models.ForeignKey(to='UserType') class UserType(models.Model): name=models.CharField(max_length=32) View: user_list=models.UserInfo.objects.all() for user in user_list: #取本表数据,涉及多表时性能会变差 select_related 主动连表查询(foreign_key),可以填入多个字段,select_related 只能进行OneToOne和ForeignKey查询,不能进行ManyToMany查询 user_list=models.UserInfo.objects.all().select_related(foreign_key字段) for user in user_list: #取当前表数据和FK关联字段数据 user_list=models.UserInfo.objects.values(...) for user in user_list: #取当前表数据和关联字段数据 prefetch_related 可以填入多个字段 user_list=models.UserInfo.objects.all().prefetch_related(foreign_key字段) [obj,obj,obj,...] 查询用户表user_list 把用户表中所有的ut_id拿到,用户类型ID[1,2,3,..],不大于UserType表总数 select * from UserType where id in [1,2,3,...] 补充: user_list=models.UserInfo.objects.all().only("name") 只取某个字段 user_list=models.UserInfo.objects.all().defer("name")排除当前字段 取符合填入字段的数据性能较好
分页
在项目目录下创建utils目录,新建page.py,写入: """ 使用方法: from utils.page import Pagination def users(request): current_page = int(request.GET.get('page',1)) total_item_count = models.UserInfo.objects.all().count() # page_obj = Pagination(current_page,total_item_count,request.path_info) page_obj = Pagination(current_page,total_item_count,'/users.html') user_list = models.UserInfo.objects.all()[page_obj.start:page_obj.end] return render(request,'users.html',{'user_list':user_list,'page_html':page_obj.page_html()}) """ from django.utils.safestring import mark_safe class Pagination(object): def __init__(self,current_page,total_item_count,base_url,per_page_count=10,show_pager_count=11): """ :param current_page: 当前页 :param total_item_count: 数据库数据总条数 :param base_url: 分页前缀URL :param per_page_count: 每页显示数据条数 :param show_pager_count: 对多显示的页码 """ self.current_page = current_page self.total_item_count = total_item_count self.base_url = base_url self.per_page_count = per_page_count self.show_pager_count = show_pager_count max_pager_num, b = divmod(total_item_count, per_page_count) if b: max_pager_num += 1 self.max_pager_num = max_pager_num @property def start(self): """ :return: """ return (self.current_page-1)* self.per_page_count @property def end(self): """ :return: """ return self.current_page * self.per_page_count def page_html(self): """ :return: """ page_list = [] if self.current_page == 1: prev = ' <li><a href="#">上一页</a></li>' else: prev = ' <li><a href="%s?page=%s">上一页</a></li>' % (self.base_url,self.current_page - 1,) page_list.append(prev) half_show_pager_count = int(self.show_pager_count / 2) # 数据特别少,15条数据=2页 if self.max_pager_num < self.show_pager_count: # 页码小于11 pager_start = 1 pager_end = self.max_pager_num + 1 else: if self.current_page <= half_show_pager_count: pager_start = 1 pager_end = self.show_pager_count + 1 else: if self.current_page + half_show_pager_count > self.max_pager_num: pager_start = self.max_pager_num - self.show_pager_count + 1 pager_end = self.max_pager_num + 1 else: pager_start = self.current_page - half_show_pager_count pager_end = self.current_page + half_show_pager_count + 1 for i in range(pager_start, pager_end): if i == self.current_page: tpl = ' <li class="active"><a href="%s?page=%s">%s</a></li>' % (self.base_url,i, i,) else: tpl = ' <li><a href="%s?page=%s">%s</a></li>' % (self.base_url,i, i,) page_list.append(tpl) if self.current_page == self.max_pager_num: nex = ' <li><a href="#">下一页</a></li>' else: nex = ' <li><a href="%s?page=%s">下一页</a></li>' % (self.base_url,self.current_page + 1,) page_list.append(nex) return mark_safe(''.join(page_list))
自定义序列化
from datetime import datetime from datetime import date import json class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self, field) data_list = [ {'name':'alex','age':19, 'ctime': datetime.now()}, {'name':'egon','age':19, 'ctime': datetime.now()}, {'name':'eric','age':19, 'ctime': datetime.now()}, {'name':'rain','age':19, 'ctime': datetime.now()} ] val = json.dumps(data_list,cls=JsonCustomEncoder) print(val)