Python自動化開發學習22-Django上
上節已經講了使用Cookie來做用戶認證,但是
Cookie的問題
缺點:敏感信息不適合放在cookie裏,敏感信息只能放在服務器端
優勢:把部分用戶數據分散的存放在每個客戶端,減輕服務端的壓力
Cookie是保存在用戶瀏覽器端的鍵值對,Session是保存在服務器端的鍵值對。
Session依賴Cookie,Cookie保存隨機字符串,憑借這個隨機字符串獲取到服務器端Session裏的內容。
用Session來優化用戶登錄:用戶登錄後,生成一個隨機字符串,通過Cookie發送給客戶端保存。服務器端維護一個字典,字典的key就是這個隨機生成的字符串,字典的value是另外一個字典,在服務器端保存各種用戶的敏感信息。
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>
上面用到了設置數據的操作和獲取數據的操作。其他的操作方法見下面整理的內容。
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)
Python自動化開發學習22-Django上