缓存是一类可以更快的读取数据的介质统称,也指其它可以加快数据读取的存储方式。一般用来存储临时数据,常用介质的是读取速度很快的内存
视图渲染有一定成本,对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数
案例分析
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
1,博客列表页
2,电商商品详情页
3,缓存导航及页脚
Django中提供多种缓存方式,如需使用需要在settings.py中进行配置
1,数据库缓存
Django可以将其缓存的数据存储在您的数据库中
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
}
}
创建缓存表
python manage.py createcachetable
2,文件系统缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',#这个是文件夹的路径
#'LOCATION': 'c:\test\cache',#windows下示例
}
}
3, 本地内存缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake'
}
}
在视图View中使用cache
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)),
]
在模板中使用
{% load cache %}
{% cache 500 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
浏览器缓存分类:
不会向服务器发送请求,直接从缓存中读取资源
1,Expires
缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点
Expires=max-age + 请求时间
Expires 是 HTTP/1 的产物,受限于本地时间,如果修改了本地时间,可能会造成缓存失效
2, Cache-Control
在HTTP/1.1中,Cache-Control主要用于控制网页缓存。比如当Cache-Control:max-age=120
代表请求创建时间后的120秒,缓存失效
横向对比 Expires VS Cache-Control
**协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
1,Last-Modified和If-Modified-Since
第一次访问时,服务器会返回
Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
浏览器下次请求时 携带If-Modified-Since这个header , 该值为 Last-Modified
服务器接收请求后,对比结果,若资源未发生改变,则返回304, 否则返回200并将新资源返回给浏览器
缺点:只能精确到秒,容易发生单秒内多次修改,检测不到
2,ETag和If-None-Match
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成
流程同上
横向对比 Last-Modified VS ETag
中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。
每个中间件组件负责做一些特定的功能。例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话将用户与请求关联起来。
他的文档解释了中间件是如何工作的,如何激活中间件,以及如何编写自己的中间件。Django 具有一些内置的中间件,你可以直接使用。它们被记录在 built-in middleware reference 中。
中间件类:
django.utils.deprecation.MiddlewareMixin
类def process_request(self, request):
执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象def process_view(self, request, callback, callback_args, callback_kwargs):
调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象def process_response(self, request, response):
所有响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse对象def process_exception(self, request, exception):
当处理过程中抛出异常时调用,返回一个HttpResponse对象def process_template_response(self, request, response):
在视图刚好执行完毕之后被调用,在每个请求上调用,返回实现了render方法的响应对象编写中间件类:
# file : middleware/mymiddleware.py
from django.http import HttpResponse, Http404
from django.utils.deprecation import MiddlewareMixin
class MyMiddleWare(MiddlewareMixin):
def process_request(self, request):
print("中间件方法 process_request 被调用")
def process_view(self, request, callback, callback_args, callback_kwargs):
print("中间件方法 process_view 被调用")
def process_response(self, request, response):
print("中间件方法 process_response 被调用")
return response
def process_exception(self, request, exception):
print("中间件方法 process_exception 被调用")
def process_template_response(self, request, response):
print("中间件方法 process_template_response 被调用")
return response
# file : settings.py
MIDDLEWARE = [
...
'middleware.mymiddleware.MyMiddleWare',
]
练习
from django.http import HttpResponse, Http404
from django.utils.deprecation import MiddlewareMixin
import re
class VisitLimit(MiddlewareMixin):
'''此中间件限制一个IP地址对应的访问/user/login 的次数不能改过10次,超过后禁止使用'''
visit_times = {} # 此字典用于记录客户端IP地址有访问次数
def process_request(self, request):
ip_address = request.META['REMOTE_ADDR'] # 得到IP地址
if not re.match('^/test', request.path_info):
return
times = self.visit_times.get(ip_address, 0)
print("IP:", ip_address, '已经访问过', times, '次!:', request.path_info)
self.visit_times[ip_address] = times + 1
if times < 5:
return
return HttpResponse('你已经访问过' + str(times) + '次,您被禁止了')
跨站请求伪造攻击
说明:
CSRF中间件和模板标签提供对跨站请求伪造简单易用的防护。
作用:
解决方案:
取消 csrf 验证(不推荐)
django.middleware.csrf.CsrfViewMiddleware
的中间件通过验证 csrf_token 验证
需要在表单中增加一个标签
{% csrf_token %}
django.core.paginator
模块中。对象的构造方法
Paginator属性
Paginator方法
Paginator异常exception
创建对象
Paginator对象的page()方法返回Page对象,不需要手动构造
Page对象属性
Page对象方法
说明:
参考文档https://docs.djangoproject.com/en/1.11/topics/pagination/
分页示例:
from django.core.paginator import Paginator
def book(request):
bks = models.Book.objects.all()
paginator = Paginator(bks, 10)
print('当前对象的总个数是:', paginator.count)
print('当前对象的面码范围是:', paginator.page_range)
print('总页数是:', paginator.num_pages)
print('每页最大个数:', paginator.per_page)
cur_page = request.GET.get('page', 1) # 得到默认的当前页
page = paginator.page(cur_page)
return render(request, 'bookstore/book.html', locals())
<html>
<head>
<title>分页显示title>
head>
<body>
{% for b in page %}
<div>{{ b.title }}div>
{% endfor %}
{# 分页功能 #}
{# 上一页功能 #}
{% if page.has_previous %}
<a href="{% url 'book' %}?page={{ page.previous_page_number }}">上一页a>
{% else %}
上一页
{% endif %}
{% for p in paginator.page_range %}
{% if p == page.number %}
{{ p }}
{% else %}
<a href="{% url 'book' %}?page={{ p }}">{{ p }}a>
{% endif %}
{% endfor %}
{#下一页功能#}
{% if page.has_next %}
<a href="{% url 'book' %}?page={{ page.next_page_number }}">下一页a>
{% else %}
下一页
{% endif %}
总页数: {{ page.len }}
body>
html>
文件上传必须为POST提交方式
表单中文件上传时必须有带有
enctype="multipart/form-data"
时才会包含文件内容数据。
表单中用标签上传文件
xxx
对应request.FILES['xxx']
对应的内存缓冲文件流对象。可通能过request.FILES['xxx']
返回的对象获取上传文件数据file=request.FILES['xxx']
file 绑定文件流对象,可以通过文件流对象的如下信息获取文件数据上传文件的表单书写方式
<html>
<head>
<meta charset="utf-8">
<title>文件上传title>
head>
<body>
<h3>上传文件h3>
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="myfile"/><br>
<input type="submit" value="上传">
form>
body>
html>
在setting.py 中设置一个变量MEDIA_ROOT 用来记录上传文件的位置
# file : settings.py
...
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/files')
在当前项目文件夹下创建 static/files
文件夹
$ mkdir -p static/files
添加路由及对应的处理函数
# file urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^upload', views.upload_view)
]
上传文件的视图处理函数
# file views.py
from django.http import HttpResponse, Http404
from django.conf import settings
import os
def upload_view(request):
if request.method == 'GET':
return render(request, 'index/upload.html')
elif request.method == "POST":
a_file = request.FILES['myfile']
print("上传文件名是:", a_file.name)
filename =os.path.join(settings.MEDIA_ROOT, a_file.name)
with open(filename, 'wb') as f:
data = a_file.file.read()
f.write(data)
return HttpResponse("接收文件:" + a_file.name + "成功")
raise Http404
访问地址: http://127.0.0.1:8000/static/upload.html