session
上节已经讲了使用Cookie来做用户认证,但是
Cookie的问题
缺点:敏感信息不适合放在cookie里,敏感信息只能放在服务器端
优势:把部分用户数据分散的存放在每个客户端,减轻服务端的压力
Cookie是保存在用户浏览器端的键值对,Session是保存在服务器端的键值对。
Session依赖Cookie,Cookie保存随机字符串,凭借这个随机字符串获取到服务器端Session里的内容。
用Session来优化用户登录:用户登录后,生成一个随机字符串,通过Cookie发送给客户端保存。服务器端维护一个字典,字典的key就是这个随机生成的字符串,字典的value是另外一个字典,在服务器端保存各种用户的敏感信息。
Django的sessiong默认是保存在数据库中的,所以要使用session需要先执行生成数据库的2条命令:
python manage.py makemigrations
python manage.py migrate
即使你没有写任何一张表,也会默认生成一些表,其中 django_session 表中就是存放session信息的。
操作
session里存放的就是键值对,所以操作起来也跟字典一样。随便写一个登录的页面,用下面的处理函数进行操作:
USER_INFO = {
‘test‘: {‘pass‘: "test123"},
‘user‘: {‘pass‘: "user123"},
}
def login(request):
if request.method == "GET":
return render(request, ‘login.html‘)
elif request.method == ‘POST‘:
user = request.POST.get(‘user‘)
pwd = request.POST.get(‘pwd‘)
user_obj = USER_INFO.get(user)
if user_obj and user_obj.get(‘pass‘) == pwd:
# 1.生成随机字符串
# 2.写到用户浏览器Cookie
# 3.把随机字符串作为key保存到session中
# 4.设置对应的value
# 理论上是要完成上面4个步骤
# 但是都封装好了,用的时候只需要1步,就能完成上面的操作,下面是设置session的值
request.session[‘username‘] = user
request.session[‘is_login‘] = True
return redirect(‘/welcome/‘)
else:
return render(request, ‘login.html‘)
def welcome(request):
# 获取session的值
if request.session.get(‘is_login‘):
return HttpResponse(‘Welcome %s‘ % request.session[‘username‘])
else:
return HttpResponse(‘登录失败‘)
登录页面:
<form action="/login/" method="POST">
<p><input type="text" name="user" placeholder="用户名"></p>
<p><input type="password" name="pwd" placeholder="密码"></p>
<p><input type="submit" value="登录"></p>
</form>
上面用到了设置数据的操作和获取数据的操作。其他的操作方法见下面整理的内容。
获取、设置、删除Session中数据:
request.session[‘k1‘]
:获取值,如果key不存在会报错request.session.get(‘k1‘,None)
:获取值,如果key不存在,在获取到默认值。默认值默认是Nonerequest.session[‘k1‘] = 123
:设置值request.session.setdefault(‘k1‘,123)
:设置默认值,就是如果这个key有值就不改变,没有值就设置为第二个参数的值del request.session[‘k1‘]
:删除值
对所有键、值、键值对的操作:
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()
iterkeys 和 keys 的区别,iterkeys 返回一个迭代器,而 keys 返回一个列表。
其他:
request.session.session_key
:获取当前用户的随机字符串。但是一般这个用不着,不过是可以获取到的request.session.clear_expired()
:将所有Session失效日期小于当前日期的数据删除。过期的session,数据库是没有自动清除的机制的。不过缓存系统是有的request.session.exists("session_key")
:检查作为参数的随机字符串在数据库中是否存在。一般也用不着,因为获取值的时候已经包含这个判断了,没有会返回Nonerequest.session.delete("session_key")
:删除参数的Session的所有数据request.session.clear()
:删除当前用户的所有Session数据。和delete方法比较,delete需要提供session_key。而clear方法会先去获取到当前用户的key,然后delete
request.session.set_expiry(value)
:设置超时时间
- 如果value是个整数,session会在些秒数后失效。
- 如果value是个datatime或timedelta,session就会在这个时间后失效。
- 如果value是0,用户关闭浏览器session就会失效。
- 如果value是None,session会依赖全局session失效策略。
下面的内容在 Lib/site-packages/django/conf/global_settings.py 这个文件里,是默认配置。要修改的话,就在我们的 settings.py 里设置一个值:
############
# SESSIONS #
############
# Cache to store session data if using the cache session backend.
SESSION_CACHE_ALIAS = ‘default‘
# Cookie name. This can be whatever you want.
SESSION_COOKIE_NAME = ‘sessionid‘ # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
# Age of cookie, in seconds (default: 2 weeks).
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Session的cookie失效日期,这里默认的是2周
# A string like "example.com", or None for standard domain cookie.
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名
# Whether the session cookie should be secure (https:// only).
SESSION_COOKIE_SECURE = False # 是否Session的cookie只支持http传输
# The path of the session cookie.
SESSION_COOKIE_PATH = ‘/‘ # Session的cookie保存的路径
# Whether to use the non-RFC standard httpOnly flag (IE, FF3+, others)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输
# Whether to save the session data on every request.
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
# Whether a user‘s session cookie expires when the Web browser is closed.
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期
# The module to store session data
SESSION_ENGINE = ‘django.contrib.sessions.backends.db‘ # 默认引擎
# Directory to store session files if using the file session module. If None,
# the backend will use a sensible default.
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址
# class to serialize session data
SESSION_SERIALIZER = ‘django.contrib.sessions.serializers.JSONSerializer‘
CSRF(跨站请求伪造)
客户端第一次发起GET请求后,不仅返回了页面,同时还会返回一串加密字符串。之后客户端再提交数据的时候,需要把之前收到的字符串一并提交,这样服务器端的CSRF就会对字符串进行验证,确认收到的数据是之前发起请求的客户端提交上来的。
无CSRF的隐患,如果没有CSRF,正常客户端提交数据是提交给当前访问的网站。但是也是可以提交到别的网站去的(你可以提交到任何地方)。虽然你可以提交过去,但是这种方式你没有目标网站给你的加密字符串的,所以开启CSRF之后,直接就把这部分请求拦截了。
开启CSRF
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局,settings.py 文件中的MIDDLEWARE(中间件)里,是一个列表,之前都是先把下面的这行注释掉的:
‘django.middleware.csrf.CsrfViewMiddleware‘,
局部
导入模块:from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
:为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。@csrf_protect
:取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
应用-Form方式提交
第一次请求获取到的字符串在哪里,如何在提交form表单的时候加上获取的字符串,从而通过CSRF的验证。做一个简单的登录页面,上节有,然后去 settings.py 文件里开启之前一直注释掉的CSRF,按下面的方式提交:
<form action="/login/" method="POST">
{# 加上下面的标签就可以了 #}
{% csrf_token %}
<p><input type="text" name="username" placeholder="用户名"></p>
<p><input type="password" name="password" placeholder="密码"></p>
<p><input type="submit" value="登录"></p>
</form>
如果form不带 csrf_token ,那么会返回403错误。这里查看客户端form标签内的源码可以发现,系统帮我们生成了一个隐藏的input标签
<input name="csrfmiddlewaretoken" type="hidden" value="I9pZVK6UYWgHHPfUQxju79pWu65xOrb793mpWXON1n4HgeTGzheJ78HHQBgu6cB8">
应用-Ajax方式提交
使用Ajax方式提交,需要先获取到csrf的字符串,客户端收到服务器端的字符串后,是保存在cookie里的,所以可以到cookie里去获取到。提交的时候也要带上这个csrf字符串,加一条请求头: headers: {‘X-CSRFtoken‘: csrftoken},
。
在原来的页面里,添加Ajax请求的按钮,并且绑定事件。验证不通过重新加载页面,验证通过则用 location.href
页面跳转:
<body>
<form action="/login/" method="POST">
{% csrf_token %}
<p><input type="text" name="user" placeholder="用户名"></p>
<p><input type="password" name="pwd" placeholder="密码"></p>
<p>
<input type="submit" value="登录">
<input id="btn" type="button" value="Ajax提交">
</p>
</form>
<script src="/static/js/jquery-1.12.4.js"></script>
<script src="/static/js/jquery.cookie.js"></script>
<script>
$(function () {
$(‘#btn‘).click(function () {
var csrftoken = $.cookie(‘csrftoken‘);
var user = $("input[name=‘user‘]").val();
var pwd = $("input[name=‘pwd‘]").val();
$.ajax({
url: ‘/login/‘,
type: ‘POST‘,
data: {‘user‘: user, ‘pwd‘: pwd, ‘ajax‘: true},
headers: {‘X-CSRFtoken‘: csrftoken},
success: function (arg) {
if(arg === ‘OK‘){
location.href = ‘/welcome/‘
}else{
location.reload()
}
}
})
})
})
</script>
</body>
处理函数也在原来的基础上添加。通过 is_ajax = request.POST.get(‘ajax‘)
判断是否是ajax提交的请求。最后在return的时候返回不同的结果,其他都不变:
def login(request):
if request.method == "GET":
return render(request, ‘login.html‘)
elif request.method == ‘POST‘:
user = request.POST.get(‘user‘)
pwd = request.POST.get(‘pwd‘)
is_ajax = request.POST.get(‘ajax‘)
user_obj = USER_INFO.get(user)
if user_obj and user_obj.get(‘pass‘) == pwd:
# 1.生成随机字符串
# 2.写到用户浏览器Cookie
# 3.把随机字符串作为key保存到session中
# 4.设置对应的value
# 但是只需要一步,就能完成上面的操作,下面是设置session的值
request.session[‘username‘] = user
request.session[‘is_login‘] = True
print(request.session)
return HttpResponse(‘OK‘) if is_ajax else redirect(‘/welcome/‘)
else:
print(‘error‘)
return HttpResponse(‘error‘) if is_ajax else render(request, ‘login.html‘)
统一设置ajax的csrf,如果页面里有多个ajax请求,可以统一进行设置。再或者比如之前的练习,已经写好了不带csrf的ajax请求,现在也不要去里面改了,用下面的方法统一加上。就是在ajax发送前,设置请求头加上X-CSRFtoken这个key并且设置值:
<script>
$(function () {
$.ajaxSetup({
beforeSend: function (xhr, settings) {
// xhr 就是 XHLHttpRequest 是一个对象
xhr.setRequestHeader(‘X-CSRFtoken‘, $.cookie(‘csrftoken‘)) // 设置请求头
}
});
$(‘#btn‘).click(function () {
// var csrftoken = $.cookie(‘csrftoken‘);
var user = $("input[name=‘user‘]").val();
var pwd = $("input[name=‘pwd‘]").val();
$.ajax({
url: ‘/login/‘,
type: ‘POST‘,
data: {‘user‘: user, ‘pwd‘: pwd, ‘ajax‘: true},
// headers: {‘X-CSRFtoken‘: csrftoken},
success: function (arg) {
if(arg === ‘OK‘){
location.href = ‘/welcome/‘
}else{
location.reload()
}
}
})
})
})
</script>
上面的代码还要再优化一个,只有POST请求需要加csrf,GET请求是不需要加的。但是在上面的代码里所有的ajax请求的前面都会在请求头加上csrf,这里要再做个判断。除了POST和GET还有其他好几种请求,一起做判断:
<script>
function csrfSafeMethod(method) {
// 下面的这几类HTTP请求是不需要加CSRF验证的,这个是官网的一个例子的用法
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$(function () {
$.ajaxSetup({
beforeSend: function (xhr, settings) {
// xhr 就是 XHLHttpRequest 是一个对象
// settings 里就是下面的$.ajax({})大括号里的内容,这里要获取type检查是否需要加csrf
if (!csrfSafeMethod(settings.type) && !this.crossDomain){
// 满足上面的条件才需要设置请求头
xhr.setRequestHeader(‘X-CSRFtoken‘, $.cookie(‘csrftoken‘)) // 设置请求头
}
}
});
$(‘#btn‘).click(function () {
// var csrftoken = $.cookie(‘csrftoken‘);
var user = $("input[name=‘user‘]").val();
var pwd = $("input[name=‘pwd‘]").val();
$.ajax({
url: ‘/login/‘,
type: ‘POST‘,
data: {‘user‘: user, ‘pwd‘: pwd, ‘ajax‘: true},
// headers: {‘X-CSRFtoken‘: csrftoken},
success: function (arg) {
if(arg === ‘OK‘){
location.href = ‘/welcome/‘
}else{
location.reload()
}
}
})
})
})
</script>
中间件
在settings.py文件里有一个列表,里面列个各种中间件,包括上面将过的csrf。下面是原始的settings.py文件里的中间件的内容:
MIDDLEWARE = [
‘django.middleware.security.SecurityMiddleware‘,
‘django.contrib.sessions.middleware.SessionMiddleware‘,
‘django.middleware.common.CommonMiddleware‘,
‘django.middleware.csrf.CsrfViewMiddleware‘,
‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
‘django.contrib.messages.middleware.MessageMiddleware‘,
‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
]
用户发起请求,到请求到达Views之前,首先要通过这写中间件。这些中间件就是一个类,经过就是要调用这些中间件类里的方法,process_request方法。请求的返回也要先通过这些中间件,调用process_response方法,然后再发送给用户。
其他名称在其他的Web框架里,也有中间件,但是名字可能叫做:管道(Pipeline),HttpHandler。
自定义中间件
先以csrf的中间件举例,代码中引用的字符串是:‘django.middleware.csrf.CsrfViewMiddleware‘ 。这个就是路径和类名。
可以到python的模块中找到这个文件和类。在 \Lib\site-packages\django\middleware\csrf.py 这个文件里有一个类 class CsrfViewMiddleware(MiddlewareMixin):
,这个类里就有process_request方法和process_response方法。并且可以看到这个类是继承了MiddlewareMixin这个类的,查看这个父类,可以看到这个类里就是调用上面的2个方法。
要写自己的中间件,就是要定义一个类,继承MiddlewareMixin。并且在类里写上process_request方法和process_response方法。
在项目下创建Middle文件夹来存放自定义的中间件,创建m1.py文件:
from django.utils.deprecation import MiddlewareMixin
class Row1(MiddlewareMixin):
def process_request(self, request):
print(‘ROW1_reuquest‘)
def process_response(self, request, response):
print(‘ROW1_response‘)
return response
class Row2(MiddlewareMixin):
def process_request(self, request):
print(‘ROW2_reuquest‘)
def process_response(self, request, response):
print(‘ROW2_response‘)
return response
上面写了2个类,就是2个中间件了。然后去setting.py里注册一下你的中间件:
MIDDLEWARE = [
‘django.middleware.security.SecurityMiddleware‘,
‘django.contrib.sessions.middleware.SessionMiddleware‘,
‘django.middleware.common.CommonMiddleware‘,
‘django.middleware.csrf.CsrfViewMiddleware‘,
‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
‘django.contrib.messages.middleware.MessageMiddleware‘,
‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
‘Middle.m1.Row1‘,
‘Middle.m1.Row2‘,
]
现在再访问你之前的页面,就能看到中间件print的信息了,并且可以看到触发的顺序:
ROW1_reuquest
ROW2_reuquest
[11/Apr/2018 11:24:04] "GET /login/ HTTP/1.1" 200 492
ROW2_response
ROW1_response
先触发request方法,并且是写在前面的中间件先触发。然后再是收到GET请求。最后触发response方法。
中间件的应用场景:通过中间件可以对所有的请求做一个统一的操作,比如黑名单过滤。
中间件中可以定义以下方法:
- process_request(self, request) :处理请求前最先处理
- process_view(self, request, callback, callback_args, callback_kwargs) :在process_request全部执行之后执行,还是从上到下
- process_template_response(self, request, response) :如果views返回对象中有render方法,则会执行
- process_exception(self, request, exception) :当views里出现异常时触发。exception就是异常的信息
- process_response(self, request, response) :返回结果后到客户端收到响应前处理
常用的就2个,其他了解一下
缓存
由于Django是动态网站,所以每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用缓存。缓存是将某个views的返回值保存至内存或者memcache中,5分钟内(默认设置)再有人来访问时,则不再去执行views中的函数,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django中提供了6种缓存方式:
- 开发调试
- 内存
- 文件
- 数据库
- Memcache缓存(python-memcached模块)
- Memcache缓存(pylibmc模块)
配置
开发调试以及默认配置
实际内部不做任何操作,就是调试用的。只有第一个引擎是必须要设置的,其他都有默认配置(上面也是照着默认设的),不写就是使用默认设置,或者写上你要修改的设置。其它缓存方式的配置也是一样的,就是把引擎换一下,再加一个 ‘LOCATION‘ 就是缓存存放的位置:
# 此为开始调试用,实际内部不做任何操作。
# 配置:
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.dummy.DummyCache‘, # 引擎
‘TIMEOUT‘: 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
‘OPTIONS‘: {
‘MAX_ENTRIES‘: 300, # 最大缓存个数(默认300,就是5分钟)
‘CULL_FREQUENCY‘: 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认1/3)
},
‘KEY_PREFIX‘: ‘‘, # 缓存key的前缀(默认空)
‘VERSION‘: 1, # 缓存key的版本(默认1)
‘KEY_FUNCTION‘: default_key_func # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
}
}
默认的 ‘KEY_FUNCTION‘ 是在 Lib\site-packages\django\core\cache\backends\base.py 文件里的default_key_func函数,就是生成一个包含 key_prefix, version, key 这3个变量的字符串返回。这里可以用我们自己的写的函数生成自己想要的样式:
def default_key_func(key, key_prefix, version):
"""
Default function to generate keys.
Construct the key used by all other methods. By default, prepend
the `key_prefix‘. KEY_FUNCTION can be used to specify an alternate
function with custom key making behavior.
"""
return ‘%s:%s:%s‘ % (key_prefix, version, key)
内存
# 此缓存将内容保存至内存的变量中
# 配置:
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‘, # ip地址和端口
}
}
# 或者是连接本地文件
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.memcached.MemcachedCache‘,
‘LOCATION‘: ‘unix:/tmp/memcached.sock‘,
}
}
# 或者是使用分布式的memcache
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.memcached.MemcachedCache‘,
‘LOCATION‘: [
‘172.19.26.240:11211‘,
‘172.19.26.242:11211‘,
]
}
}
# 或者是使用分布式的memcache,还可以带权重(权重是memcache做的)
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.memcached.MemcachedCache‘,
‘LOCATION‘: [
(‘172.19.26.240:11211‘, 2),
(‘172.19.26.242:11211‘, 3),
]
}
}
Memcache缓存(pylibmc模块)
和上面差不多,只是用的是不同的python模块,所以引擎要变一下。
# 此缓存使用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‘,
]
}
}
# 权重也是有的
应用
准备测试环境
使用文件的形式来测试,在项目下创建一个cache文件夹来存放缓存文件,配置如下:
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.filebased.FileBasedCache‘,
‘LOCATION‘: os.path.join(BASE_DIR, ‘cache‘), # 缓存文件的路径
}
}
然后写一个显示当前时间戳的处理函数:
def cache(request):
import time
ctime = time.time()
return render(request, ‘cache.html‘, {‘ctime‘: ctime})
页面只要把时间戳显示出来:
<body>
<h1>{{ ctime }}</h1>
<h1>{{ ctime }}</h1>
</body>
现在访问页面会看到一个时间戳,并且刷新页面时间戳也会刷新,说明没有走缓存。因为上面我们只是把缓存配置上了,还是还没应用上。
全站应用缓存
这个太粗糙了,下面有更加精细的设置。
要给所有的请求都应用上缓存,这个功能适合放在中间件里。中间件里需要写2个中间件
查缓存,收到用户请求先查找缓存,如果命中则直接返回缓存的内容。这个中间件要放在后面的位置,只有通过了一系列验证的中间件后才能给用户返回数据。
写缓存,返回给用户之前没有缓存的内容的同时,需要把这个内容写到缓存里去,那么下次就有缓存了。这个中间件要放在靠前的位置,通过了一系列其他中间件加工之后,把最终返回的内容缓存起来。
MIDDLEWARE = [
‘django.middleware.cache.UpdateCacheMiddleware‘,
# 其他中间件...
‘django.middleware.cache.FetchFromCacheMiddleware‘,
]
视图应用缓存1:处理函数前加装饰器
from django.views.decorators.cache import cache_page
@cache_page(10) # timeout必填,单位秒
def func(request):
pass
视图应用缓存2:修改urls.py的
from django.views.decorators.cache import cache_page
urlpatterns = [
path(‘admin/‘, admin.site.urls),
path(‘cache/‘, cache_page(5)(views.cache)),
]
局部视图缓存:
就是在模板语言里声明,页面里的哪些内容是要应用缓存的
<body>
{#要应用缓存,先load一下cache#}
{% load cache %}
<h1>{{ ctime }}</h1>
{#然后在需要应用缓存的内容的前后加上标签#}
{#下面的c1是自定义的key,这里缓存的值的key貌似无法根据配置自动生成#}
{% cache 5 c1 %}
<h1>{{ ctime }}</h1>
{% endcache %}
</body>
首先要load缓存,然后在要应用缓存的内容前后用标签包起来。这里需要定义timeout时间和key。
这个局部的应用场景还是很多的,只缓存页面中不会频繁发生变化的内容,而经常会变的内容则不缓存
信号
Django中提供了“信号调度”,用于在框架执行操作时解耦。通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。
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 : 创建数据库连接时,自动触发
使用之前先要把对应的模块导入:
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 class_prepared
from django.db.models.signals import pre_migrate, post_migrate
from django.core.signals import request_started
from django.core.signals import request_finished
from django.core.signals import got_request_exception
from django.test.signals import setting_changed
from django.test.signals import template_rendered
from django.db.backends.signals import connection_created
注册函数1
将函数名作为参数完成注册
# 导入模块
from django.core.signals import request_started
# 准备好要触发的函数
def callback(sender, **kwargs):
print(‘callback‘)
print(sender, kwargs)
request_started.connect(callback) # 注册你的函数
# pre_init.connect(callback2) # 可以注册多个,先注册的先执行
注册函数2
为函数加上装饰器也能完成注册:
# 导入模块
from django.core.signals import request_started
from django.dispatch import receiver
# 准备好要触发的函数,并且加上装饰器
@receiver(request_started)
def callback(sender, **kwargs):
print(‘callback‘)
print(sender, kwargs)
自定义信号
要自定义信号需要3步:
- 定义信号 :这里单独创建一个文件(名字随意比如sg.py)来存放自定义的信号
- 注册信号 :定义完成好,直接就注册吧,注册方法和注册内置信号一样
- 触发信号 :在你要触发的位置,执行一个send()方法。比如下面的例子中是在处理函数中加入了这个信号的触发
内置信号只需要注册。而自定义的信号,需要在注册前先定义好这个信号。之后去你需要的位置加上触发信号的send()方法:
# sg.py 里的内容
# 第一步:定义信号
import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"]) # 这里要求2个参数
# 第二步:注册信号
def callback(sender, **kwargs):
print(‘callback‘)
print(sender, kwargs)
pizza_done.connect(callback)
# views.py 里的内容,当然可以是任何地方
# 第三步:触发信号
# 导入信号
from sg import pizza_done
def my_sig(request):
# 发送信号,第一个参数是发送者,后面是你自定义的函数要求的参数
pizza_done.send(sender=‘seven‘, topping=123, size=456)
原文地址:http://blog.51cto.com/steed/2104127