本篇要点:
1.如何通过缓存做出更快的做出响应?
2.什么是中间件?
3.网站的分页功能如何实现?
4.生成csv文件
1. 缓存
什么是缓存?
- 一类可以更快的读取数据的介质或加快数据读取的存储方式;
- 一般用于存储临时数据;
- 常用介质的是读取速度很快的内存。
网站的响应速度限制门槛:
网速不是主要门槛,主要在后端的数据库中,由于数据表过多,或sql查询的方式不当,导致后台数据传入前端显示过慢。
Django提供的缓存优化方案:
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page ,
save the generate page in the cacher(for next time)
return the generated page;
缓存场景:
- 博客列表,电商商品详情页
- 场景特点:缓存的地方,数据变动频率较少
django中配置缓存
1.数据库缓存
缓存介质是后端数据库,尽管缓存介质没有更换,但当把一次负责查询的结果直接存储到表中,避免重复进行复杂查询。
settings.py文件中的CACHES字段相关配置
# 数据库缓存配置-数据库缓存 (需要手动创建cache表)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
'TIMEOUT': 300, #缓存保存时间 单位秒, 默认值为300,
'OPTIONS': {
'MAX_ENTRIES': 300, #缓存最大数据条数
'CULL_FREQUENCY': 2, #缓存条数达到最大值时,删除1/x的缓存数据
}
}
}
2.浏览器缓存
浏览器缓存的工作流程图:
浏览器缓存分为两种类型:强缓存、协商缓存
强缓存
不会向服务器发送请求,直接从缓存中读取资源
- 响应头-expires :指定资源的过期时间
- 响应头-Cache-Control : 请求创建时间后的多少秒,缓存失效(优先判断该字段)
协商缓存
强缓存中的数据一旦过期,还需要跟服务器进行通信,从而获取最新数据(对于一些大文件)
协商缓存-方式一
- 响应头-Last-Modified :文件的最近修改时间
- 请求头-if-Modified-Since : 对服务器发送协商,服务端返回304响应【代表缓存继续使用】,200响应代表缓存不可用【响应体为最新资源】
协商缓存-方式二
- ETag响应头 -返回当前资源文件的一个唯一标识(哈希值)
- If-None-Match请求头 - 协商结果与上面策略一致
3.其他类型缓存
- 数据缓存到服务器内存中(测试型,一般都存放在redis中)
- 文件系统缓存(不推荐)
怎么使用缓存?
1.整体缓存策略
- 视图函数中
使用方法:在视图函数前添加装饰器@cache_page(参数);后面参数表示当前视图函数的缓存存活多久;
作用:缓存中有请求的数据则直接返回,没有则走视图函数并把结果返回给装饰器以便下次存储。
示例代码中前面的缓存数据存活15秒 ,从获取时间戳到15秒之前都不会改变,直到到15秒后才会改变。
@cache_page(15)
def test_cache(request):
t = time.time()
return HttpResponse('t is %s' %(t))
- 路由中
from jdango.views.decorators.cache import cache_page
urlpatterns = [
path('foo/', cache_page(15)(my_view)),
]
2.局部缓存策略
缓存API(应用程序编程接口)的使用,调用方式和字典类似
# 方式1
from django.core.cache import caches
cache1 = caches['myalias']
# 方式2
from django.core.cache import cache
# 相对于直接引入CACHES配置项中的'default'项
cache的存储 、获取、删除方法:
# 存储缓存
cache.set(key, value, timeout)
cache.add(key, value) # 只有在key不存在时生效
cache.set_many(dict, timeout) # 返回插入不成功的key的数组
# 获取缓存
cache.get(key)
cache.get_many(key_list)
cache.get_or_set(key,value,timeout)
#删除缓存
cache.delete(key)
cache.delete_many(key_list)
2. 中间件
什么是中间件?
- django请求、响应的钩子框架,一个轻量级的、低级的‘插件’系统,用于全局改变Django的输入或输出;
- 过滤响应,过滤路由,过滤视图等,相当与收费站;比如在请求到达主路由之前在前面置于中间件;
- 中间件以类的形式体现,每个中间件组件负责做一些特定的功能。
中间件的原理图
如何编写中间件
1.编写中间件
创建与manage.py同级目录的middleware文件夹,再在里面创建__init__.py和mymiddleware.py(自定义文件名)模块,之后的中间件都在自定义文件名模块中书写;
中间件书写要求:
- 中间件必须继承django.utils.deprecation.MiddlewareMixin类;
- 中间件须实现下列五种方法中的一个或多个。
process_request(self, request)
#执行路由之前会被调用,在每一个请求上调用,返回None或HttpResponse对象(被阻止)
process_view(self, request, callback, callback_args, callback_kwargs)
#调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象
#callback视图函数,callback_args位置传参,callback_kwargs关键字传参
process_response(self, request, response)
# 所有响应返回浏览器被调用,在每个请求上调用,返回HttpResponse对象
process_exception(self, request, exception)
# 当处理过程中抛出异常时调用, 返回一个HttpResponse对象
process_template_reponse(self, request, response)
# 在视图函数执行完毕且视图返回对象中包含render方法时被调用,该方法需要返回实现render方法的响应对象
上述大多数方法返回None时表示忽略当前操作进行下一项事件
返回HttpResponse对象时表示此请求结束,直接返回给客户端
2.注册中间件
#settings.py
MIDDLEWARE=[
'middleware.mymiddleware.MyMW',
]
django调用中间件类是以注册中间件顺序从上到下处理请求,再从下到上返回响应。
案例
使用中间件实现强制某个IP地址只能向/test开头的地址,发送5次请求
class VisitLimit(MiddlewareMixin):
visit_times = {}
def process_request(self, request):
# 访问者的地址和路由
ip_address = request.META['REMOTE_ADDR']
path_url = request.path_info
# 使用正则匹配
if not re.match('^/test', path_url):
return
times = self.visit_times.get(ip_address, 0)
print('ip', ip_address, '已经访问', times)
self.visit_times[ip_address] = times + 1
if times < 5:
return
return HttpResponse('您已经访问过' + str(times)+ '次, 禁止访问')
注意:如果想要访问次数撤销、重新计时的话,可以重启本地服务器,把相当与内存值(变量)的visit_times的重置
3. CSRF攻击-跨站伪造请求攻击
利用了Cookies中保存了登录状态且自动提交
解决方案:
- 打开settings中MIDDLEWARE中Csrf是否打开
- 模版中,form标签中添加标签{% csrf_token %}
如果某个视图不需要django进行csrf保护,可以用装饰器关闭对此视图的检查
@csrf_exempt
def my_view(request):
return HttpResponse('hi')
4. 分页功能
分页功能的作用
- 方便阅读
- 减少数据提取量,减轻服务器压力
Django如何实现分页功能?
Paginator类可以方便的实现分页功能(负责分页数据整体的管理)
Paginator对象
对象的构造方法
paginator = Paginator(object_list, per_page)
参数的含义:
- object_list需要分类数据的对象列表
- per_page每页数据个数
Paginator属性
- count 分页数据的对象总数
- num_pages 分页后的页面总数
- page_range 从一开始的range对象
- per_page 返回第二个参数
Page对象
由于Paginator对象只能处理分页数据整体,不能对某一页进行管理,这里我们引用page对象,他可以负责具体某一页的数据的管理
对象的构造方法
page1 = paginator对象.page(页码)
生成page对象-管理某一页的数据的管理
Page对象属性
- object_list 当前页上所有的数据对象的列表
- number 当前页的序号,从1开始
- paginator 当前page对象相关的Paginator对象
案例
编写视图函数
def test_page(request):
# test_page/4
# test_page?page=1
page_num = request.GET.get('page', 1)
all_data = ['a', 'b', 'c', 'd', 'e']
paginator = Paginator(all_data, 2)
c_page = paginator.page(int(page_num))
return render(request, 'test_page.html', locals())
模版中html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>分页</title>
</head>
<body>
{% for p in c_page %}
<p>
{{ p }}
</p>
{% endfor %}
{% if c_page.has_previous %}
<a href="/test_page?page={{ c_page.previous_page_number }}">往上走</a>
{% else %}
往上走
{% endif %}
{% for p_num in paginator.page_range %}
{% if p_num == c_page.number %}
{{ p_num }}
{% else %}
<a href="/test_page?page={{ p_num }}">{{ p_num }}</a>
{% endif %}
{% endfor %}
{% if c_page.has_next %}
<a href="/test_page?page={{ c_page.next_page_number }}"> 往下去 </a>
{% else %}
往下去
{% endif %}
</body>
</html>
实现效果:
5. 生成csv文件
- 逗号分隔值(字符分隔符)
- 可被常见制表工具,如excel等直接进行读取
csv编写
import csv
with open('csv.csv', 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['a','b','c'])
#简单描述一下,具体内容可上其他教学内容查询
csv文件下载
- 响应Content-Type类型修改为text/csv
- 响应会获得一个额外的Content-Disposition标头,它将被浏览器用于开启"另存为"对话框
案例
只需在上一个案例的html文件中的body标签内加入以下代码:
<a href="/make_page_csv?page={{ c_page.number }}">生成csv文件</a>
实现效果: