六、补充内容
6.1 缓存
-
定义:缓存是一类可以更快的读取数据的介质统称,也指可以加快数据读取的存储方式。一般用来存储临时数据,常用介质是读取速度很快的内存
-
意义:视图渲染有一定成本,数据库的频繁查询过高,所以对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数;用户拿到响应的时间成本会更低
-
案例分析
from django.shortcuts import render def index(request): # 时间复杂度极高的渲染 book_list = Book.objects.all() #->此处假设耗时2s return render(request, 'index.html', locals())
-
优化思想
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 generated page in the cache(for next time) return the generated page
-
场景
- 博客列表页
- 电商商品详情页
-
数据库缓存
将缓存的数据存在数据库中
配置样例:
CACHES = {
‘default’: {
‘BACKEND’: ‘django.core.cache.backends.db.DatabaseCache’,
‘LOCATION’:
‘my_cache_table’,
‘TIMEOUT’: 300,
‘OPTIONS’: {
‘MAX_ENTRIES’: 300, #缓存最大数据条数
‘CULL_FREQUENCY’: 2, #缓存条数达到最大值时,删除1/x的缓存数据
}
}
}
-
本地内存缓存
数据缓存到服务器内存中
配置样例:
CACHES = {
‘default’: {
‘BACKEND’: ‘django.core.cache.backends.locmem.LocMemCache’,
‘LOCATION’: ‘unique-snowflake’
}
}
-
文件系统缓存
将缓存的数据存储到本地文件中
配置样例:
CACHES = {
‘default’: {
‘BACKEND’: ‘django.core.cache.backends.filebased.FileBasedCache’,
‘LOCATION’: ‘c:\test\cache’ #windows示例,文件夹路径
}
}
6.2 整体缓存策略
样例:
from django.views.decorators.cache import cache_page
@cache_page(30) -> 单位s
def my_view(request):
...
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/', cache_page(60)(my_view))
]
6.3 局部缓存策略
先引入cache对象
方式1:使用caches[‘CACHE配置key’]导入具体对象
from django.core.cache import caches
cache1 = caches['myalias']
cache2 = caches['myalias_2']
方式2:
from django.core.cache import cache相当于直接引入CACHES配置项中的’default’项
缓存api的使用
-
cache.set(key, value, timeout) - 存储缓存
key:缓存的key,字符串类型
value:Python对象
timeout:缓存存储时间(s),默认为CACHES中的TIMEOUT值
返回值:None
-
cache.get(key) - 获取缓存
key:缓存的key
返回值:为key的具体值,如果没有数据,则返回None
-
cache.add(key, value) - 存储缓存,只在key不存在时生效
返回值:True[存储成功] or False[存储失败]
-
cache.get_or_set(key, value, timeout)
返回值:value
-
cache.set_many(dict, timeout) - 批量存储缓存
dict: key和value的字典
timeout: 存储时间(s)
返回值: 插入不成功的key的数组
-
cache.get_many(key_list) - 批量获取缓存数据
key_list: 包含key的数组
返回值: 取到的key和value的字典
-
cache.delete(key) - 删除key的缓存数据
返回值: None
-
cache.delete_many(key_list) - 批量删除
返回值: None
强缓存
不会向服务器发送请求,直接从缓存中读取资源
1. 响应头 - Expires
定义:缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点
样例:Expires:Thu, 02 Apr 2030 05:14:08 GMT
2. 响应头 - Cache - Control
在HTTP/1.1中国,Cache-Control主要用于控制网页缓存。比如当Cache-Control:max-age=120
代表请求创建时间后的120秒,缓存失效
说明:目前服务器都会带着这两个头同时响应给浏览器,浏览器优先使用Cache-Control
协商缓存
强缓存中的数据一旦过期,还需要跟服务器进行通信,从而获取最新数据;思考?如果强缓存的数据是一些静态文件,大图片等
解答:考虑到大图片这类比较费带宽且不易变化的数据,强缓存时间到期后,浏览器会去服务器协商,当前缓存是否可用,如果可用,服务器不必返回数据,浏览器继续使用原来缓存的数据,如果文件不可用,则返回最新数据
-
Last-Modified响应头和If-Modified-Since请求头
说明:
- Last-Modified为文件的最近修改时间,浏览器第一次请求静态文件时,服务器如果返回Last-Modified响应头,则代表该资源为需协商的缓存
- 当缓存到期后,浏览器将获取到的Last-Modified值作为请求头If-Modified-Since的值,与服务器发请求协商,服务器端返回304响应码[响应头为空],代表缓存继续使用,200响应码代表缓存不可用[响应体为最新资源]
-
ETag响应头和If-None-Match请求头
说明:
- Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(有服务器生成),主要资源有变化,Etag就会重新生成
- 资源到期后,浏览器将Etag响应头的值作为If-None-Match请求头的值,向服务器发请求协商;服务器接到请求头后,比对文件标识,不一致则认为资源不可用,返回200响应码[响应体为最新资源];可用则返回304响应码
6.4 中间件
-
中间件是Django请求/响应处理的钩子框架。它是一个轻量级的、低级的"插件"系统,用于全局改变Django的输入或输出
-
中间件以类的形式体现
-
每个中间件组件负责做一些特定的功能。例如,Django包含一个中间件组件AuthenticationMiddleware,它使用会话将用户与请求关联起来
-
中间件类须继承自django.utils.deprecation.MiddlewareMixin类
-
中间件类须实现下列五个方法中的一个或多个:
-
process_request(self, request)
执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象
-
process_view(self, request, callback, callback_args, callback_kwargs)
调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象
-
process_response(self, request, response)
所有响应返回浏览器被调用,在每个请求上调用,返回HttpResponse对象
-
process_exception(self, request, exception)
当处理过程中抛出异常时调用,返回一个HttpResponse对象
-
process_template_response(self, request, response)
在视图函数执行完毕且试图返回的对象汇总包含render方法时被调用;该方法需要返回实现了render方法的响应对象
-
-
注:中间件中的大多数方法在返回None时表示忽略当前操作进入下一项事件,当返回HttpResponse对象时表示此请求结束,直接返回给客户端
-
settings.py中需要注册一下自定义的中间件
# file: settings.py MIDDLEWARE = [...]
-
注意:配置为数组,中间件被调用时以’先上到下’再’由下到上’的顺序调用
练习
用中间件实现强制某个IP地址只能向/test开头的地址,发送5次请求提示:
request.META[‘REMOTE_ADDR’]可以得到远程客户端的IP地址
request.path_info可以得到客户端访问的请求路由信息
CSRF攻击
CSRF - 跨站伪造请求攻击
某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登录过的用户在浏览器上的认证信息试图在你的网站上完成某些操作,这就是跨站请求伪造(CSRF,即Cross-Site Request Forgey)
CSRF防范
- django采用’比对暗号’机制防范攻击
- Cookies中存储暗号1,模板中表单里藏着暗号2,用户只有在本网站下提交数据,暗号2才会随表单提交给服务器,django对比两个暗号,对比成功,则认为是合法请求,否则是违法请求 - 403响应码
配置步骤:
-
settings.py中确认MIDDLEWARE中django.middleware.csrf.CsrfViewMiddleware是否打开
-
模板中,form标签下添加如下标签
{% csrf_token %}
特殊说明:
如果某个视图不需要django进行csrf保护,可以用装饰器关闭对此视图的检查
样例:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def my_view(request):
return HttpResponse('hello world')
6.5 分页
- 分页是指在web页面有大量数据需要显示,为了阅读方便在,每个页中只显示部分数据
- 优点:
- 方便阅读
- 减少数据提取量,减轻服务器压力
- Django提供了Paginator类可以方便地实现分页功能
- Paginator类位于
django.core.paginator
模块中
Paginator对象
负责分页数据整体的管理
对象的构造方法
paginator = Paginator(object_list, per_page)
-
参数
- object_list 需要分类数据的对象列表
- per_page 每页数据个数
-
返回值
- Paginator的对象
-
Paginator属性
- count: 需要分页数据的对象总数
- num_pages: 分页后的页面总数
- page_range: 从1开始的range对象,用于记录
- per_page: 每页数据的个数
-
Paginator方法
paginator对象.page(number)
- 参数number为页码信息(从1开始)
- 返回当前number页对应的页信息
- 如果提供的页码不存在,抛出InvalidPage异常
-
Paginator异常exception
InvalidPage: 总的异常基类,包含以下两个异常子类
- PageNotAnInteger: 当向page()传入一个不是整数的值时抛出
- EmptyPage: 当向page()提供一个有效值,但是那个页面上没有任何对象时抛出
Page对象
定义:负责具体某一页的数据的管理
创建对象
Paginator对象的page()方法返回Page对象
page = paginator.page(页码)
Page对象属性
- object_list: 当前页上所有数据对象的列表
- number: 当前页的序号,从1开始
- paginator: 当前page对象相关的Paginator对象
Page对象方法
- has_next(): 如果有下一页返回True
- has_previous(): 如果有上一页返回True
- has_other_pages(): 如果有上一页或下一页返回True
- next_page_number(): 返回下一页的页码,如果下一页不存在,,抛出InvalidPage异常
- previous_page_number(): 返回上一页的页码,如果上一页不存在,抛出InvalidPage异常
6.6 生成csv文件
逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分割值,因为分割字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)
说明:可被常见制表工具,如excel等直接进行读取
Python提供了内建库 - csv;可直接通过该库操作csv文件
案例如下:
import csv
with open('eggs.csv', 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['a','b','c'])
在网站上,实现下载CSV,注意如下:
- 响应Content-Type类型需修改为text/csv。这告诉浏览器该文档是CSV文件,而不是HTML文件
- 响应会获得一个额外的Content-Disposition标头,其中包含CSV文件的名称。它将被浏览器用于开启“另存为…”对话框
import csv
from django.http import HttpResponse
from .models import Book
def make_csv_view(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment;filename="mybook.csv"'
all_book = Book.objects.all()
writer = csv.writer(response)
writer.writerow(['id', 'title'])
for b in all_book:
writer.writerow([b.id, b.title])
return response
练习
结合分页功能,在test_page页面中添加’生成csv’的链接,在指定页中点击该链接,生成当前页的csv数据,供用户下载