Django
一、中間件
django請求的生命週期
完整週期
可以看出中間件就是在實現請求之前,django或者我們自定義的對該請求的一些規則,而且是全局的,因爲中間件的觸發實在路由分發之前,比如某ip,某設爲不能訪問
django已經定義好的中間件
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',
]
如果我們看這些類會發現,他們都繼承了一個類MiddlewareMixin
class MiddlewareMixin:
def __init__(self, get_response=None):
self.get_response = get_response
super().__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'): # 如果有process_request方法就執行
response = self.process_request(request)
response = response or self.get_response(request)
if hasattr(self, 'process_response'): # 如果有process_response方法就執行
response = self.process_response(request, response)
return response
- 所以,我們可以這樣定義自己的類
from django.utils.deprecation import MiddlewareMixin
class MyMW(MiddlewareMixin):
def process_request(self, request):
print('1:', 'mymdwa-->process_request')
def process_response(self, request, response):
print('1:', 'mymdwa-->process_response')
return response # process_response是需要返回值的
class Mymw(MiddlewareMixin):
def process_request(self, request):
print('2:', 'mymdwa-->process_request')
def process_response(self, request, response):
print('2:', 'mymdwa-->process_response')
return response
這裏需要注意的是:process_response是需要返回值的
- 然後我們將自定義的中間件加入到settings中
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',
'app01.md.mdware.MyMW', # 自定義中間件
'app01.md.mdware.Mymw', # 自定義中間件
]
- 接着做一次get請求,我們可以看到打印結果:
Views是我在views函數中加入的print(‘Views’)
這裏反應的結果就是django先處理一個個request函數,如果一直通過,就會到達views函數,最後再將render的response逆序一層層地給中間件,最後傳遞到前端
可以發現這裏的request和response其實就是請求頭和相應結果
既然這個process_request(self, request)函數是用來處理請求頭的,那如果該請求沒有符合要求,我們就要給用戶返回一個錯誤頁面,或者請求結果
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMW(MiddlewareMixin):
def process_request(self, request):
if 允許請求:
print('1:', 'mymdwa-->process_request')
else:
return HttpResponse('403')
def process_response(self, request, response):
print('1:', 'mymdwa-->process_response')
return response
class Mymw(MiddlewareMixin):
def process_request(self, request):
if 允許請求:
print('2:', 'mymdwa-->process_request')
else:
return HttpResponse('403')
def process_response(self, request, response):
print('2:', 'mymdwa-->process_response')
return response
在process_request()中加上未允許請求時的返回結果即可,1.7版本(目前最新2.11)之後,有一箇中間件未通過,返回錯誤結果後,後面的中間件將不再執行。
- 其他
中間件的本質是,請求到達中間件時,先執行父類MiddlewareMixin的__call__方法,檢測是否有process_request()和process_response()方法,再跟着上面講述的順序執行。
所以我們也可以自定義這個父類,方便我們寫代碼。
我們自己可以寫一個類:
class MyMiddlewareMixin:
def __init__(self, get_response=None):
self.get_response = get_response
super().__init__()
def __call__(self, request):
response = None
if hasattr(self, 'my_process_request'):
response = self.process_request(request)
response = response or self.get_response(request)
if hasattr(self, 'my_process_response'):
response = self.process_response(request, response)
return response
這裏修改了父類的名字和請求函數的名字,以後我們的中間件類就可以繼承MyMiddlewareMixin,其中的函數名也可以改成my_process_request()和my_process_response(),當然這裏只是舉一個例子,類名和函數名都可以根據自己習慣定義。
- 中間件的另外幾個函數
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMW(MiddlewareMixin):
def process_request(self, request):
if 允許請求:
print('1:', 'mymdwa-->process_request')
else:
return HttpResponse('403')
def process_response(self, request, response):
print('1:', 'mymdwa-->process_response')
return response
def process_view(self, request, view_func, view_args, views_kwargs):
"""
在所有process_request函數執行完後,再從頭依次執行view函數
request: 請求頭,
view_func: views.py的對應處理函數,
view_args: view_func()的參數
view_kwargs: view_func()的參數
"""
print('1:', 'mymdwa-->process_view')
def process_exception(self, request, exception):
"""views.py的函數運行異常時逆序執行此函數"""
return HttpResponse('請求出錯.') # 遇到return後又從最後一箇中間件的process_response函數逆序執行,直到返回給前端
def process_template_response(self, request, response):
"""不常用"""
pass
二、CSRF
跨站請求僞造
在第一次請求的時候django服務器會給前端返回一個csrf-cookies,以後post請求時必須帶上這個csrf才能通過這個csrfMiddleware,我們有幾種方式來實現:
- Form請求:在form表單下添加{% csrf_token %}
- ajax請求:給ajax配置請求頭:
// 預配置
$.ajaxSteup({
beforeSend:funtin(xhr, setting){
xhr.setRequestHeader('X-CSRFToken', $.cookie('csrf'));
}
});
// ajax請求
$('.submit-btn').click({
$.ajax({
url: '/url/',
type: 'POST',
data: {'username': 'wolf'},
success: function(arg){},
})
})
- ajax請求:在發送的data數據中加上csrf:
// ajax請求
$('.submit-btn').click({
$.ajax({
url: '/url/',
type: 'POST',
data: {'username': 'wolf', 'csrfmiddlewaretoken': '{{ csrf_token }}'},
success: function(arg){},
})
})
- 因爲中間件是全局的,在views.py中可以設置裝飾器,設置某些函數必須通過csrf驗證,而全站不用
# 導入裝飾器
from django.views.decorators.csrf import csrf_exempt, csrf_protect
# 添加裝飾器,這個函數用,其他的不用
@csrf_protect
def login(request):
from django.conf import settings # django所有的配置
print(settings.CSRF_HEADER_NAME) #
return render(request, 'html')
@csrf_exempt # 添加排除裝飾器,這個函數不用,其他的都用
def logout(request):
return render(request, ''html)
一般用全站,只是少部分不用,可以使用@csrf_exempt
三、緩存
如果每次請求都去訪問數據庫,耗時將會很明顯,最好的解決方法就是用緩存:將某個views函數的返回值保存在內存(可以是其他機器)或者memcache中,設定時間內再有人來訪問,就直接從內存或redis中獲取,而不再去操作數據庫。優勢是快,劣勢是不能實時更新,我們永遠只能獲取到大約5分鐘以前的數據。
django的6種緩存方式
- 開發調試
- 內存
- 文件
- 數據庫
- memcache緩存(python-memcached模塊)
- memcache緩存(pylibmc模塊)
一般訪問量大,更新頻率低的適合緩存
1、配置
a、開發調試
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎
'TIMEOUT': 300, # 緩存超時時間(默認300,None表示永久,0表示立即過期)
'OPTIONS': {
'MAX_ENTRIES': 300, # 最大緩存數(默認300)
'CULL_FREQUENCY': 3, # 緩存達到最大後,刪除緩存的個數比例,默認爲3,即刪除1/3
}
'KEY_PREFIX': '', # 緩存key的前綴,默認爲空
'VERSION': 1, # 緩存key的版本,默認爲1
'KEY_FUNCTION':函數名 # 生成key的函數(默認函數會生成爲:[前綴:版本:key])
}
}
# 自定義key
def my_key()
b、內存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎
'LOCATION': 'unique-snowflake',
}
}
# 其他配置同開發調試
c、文件
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': '/var/temp/cache', # 文件路徑
}
}
# 其他配置同開發調試
d、數據庫
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': 'my_cache_table', # 數據庫表明
}
}
# 其他配置同開發調試
e、Memcache緩存(python-memcached模塊)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': '127.0.0.1:10001', # 另外一臺電腦的ip: port,socket訪問
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': 'unix:/tmp/memcached.soc', # 本機文件
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': [
('172.168.1.1:9000',10) # 後面數字爲權重,值越大,緩存到這臺機器的概率越大
('172.168.1.2:9000',100)
] # 多臺機器
}
}
# 其他配置同開發調試
f、Memcache緩存(pylibmc模塊)
"""同上"""
2. 應用
先將上面的配置添加到settings.py文件中
a. 本頁緩存
from django.views.decorators.cache import cache_page
import time
@cache_page(10) # 加入緩存裝飾器,這個views函數對應的頁面就會緩存,參數爲緩存時間/s
def cac(request):
t = time.time()
return HttpResponse(t)
b. 局部緩存
{% load cache %}
{% cache 5000 key %} <!--緩存10秒,緩存文件爲-->
緩存內容
{% cendcache %}
c. 全站緩存
訪問進來時,爲了不訪問views函數直接返回緩存數據,我們就需要將檢測是否返回緩存的函數寫在我們前面所講的process_view()中間件函數中,而對於是否對此次請求返回數據,需要經過中間件的審覈,所以這個process_view()函數須要放在中間件的最後一步;
而對於緩存內容只是之前views.py函數所返回的response,我們是可能在中間件中對他進行了一些改變的,所以process_response()函數須要放在中間件的第一個位置。
這樣的兩個中間件django已經幫我們寫好了。
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
# 其他中間件
'django.middleware.cache.FetchFromCacheMiddleware',
]
3. session使用緩存
在session配置中添加下面代碼即可
SESSION_CACHE_ALIAS = 'sessions'
四、信號
Django預留的hook
讓django在執行一些操作之前或者之後自動觸發一些事件,比如記錄數據庫的增刪改查操作,
- 內置信號
a. hook名稱
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 # 創建數據庫連接時,自動觸發
b. 導入方法:
from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception
from django.db.models.signals import class_prepared
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 pre_migrate, post_migrate
from django.test.signals import setting_changed
from django.test.signals import template_rendered
from django.db.backends.signals import connection_created
# 註冊信號
def my_func(sender,**kwargs):
"""sender: django給我們提供的一些信息"""
print('Do my func first')
reqyest_finished.connect(my_func) # 即在請求結束後執行my_func()方法
- 因爲在django項目開啓時就要註冊這些信號,所以寫在項目的__init__.py文件中,或者寫在其他py文件中,在__init__.py中導入
- 一個hook可以添加多個函數
- 自定義信號
a. 定義信號
import django.dispatch
my_signal = django.dispatch.Signal(providing_args=['x', 'y']) # x,y參數自定義
b. 註冊信號
def callback(sender, **kwargs):
print('my_signal')
my_signal.connect(callback)
c. 觸發信號
在自己寫的views等函數中觸發
from path import my_signal
my_signal.send(sender='name', x='x', y='y')
其實信號就相當於自定義的模塊,方便調用,當我們遇到可能需要靈活調用的功能時就可以選擇信號
五、BootStrap(模板) - 響應式+模板
集成了css、js的一個文件夾
響應式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>響應式</title>
<style>
/* 監聽瀏覽器寬度,當寬度小於700px時,改變對應的樣式
這裏當瀏覽器寬度大於700px時,h1標籤爲黑色字體,寬度小於700px時,字體會變成紅色 */
@media(max-width: 700px){
.response-style{
color: red;
}
}
</style>
</head>
<body>
<h1 class="response-style">響應式</h1>
</body>
</html>
先引入jQuery再引入boostrapjs
–> 企業官網(延遲加載+組合搜索)