Django 自訂裝飾器的運作原理與編寫方法
裝飾器是在 Django 中為視圖函數或類別輕鬆添加共通邏輯的強大工具。本文將逐步解釋裝飾器的 運作原理 和 編寫方法,幫助您親自編寫自訂裝飾器。
1. 裝飾器是什麼?
裝飾器是 Python 的 高階函數。即,它接受一個函數作為參數並返回一個 新的函數。
- 裝飾器的角色:
- 可以包裹函數以添加或修改特定功能。
- 使代碼可重用且簡潔。
2. 裝飾器的基本運作原理
為了理解裝飾器的運作原理,我們來看一個簡單的範例。
def simple_decorator(func):
def wrapper():
print("在函數執行前")
func()
print("在函數執行後")
return wrapper
@simple_decorator
def say_hello():
print("你好!")
say_hello()
輸出結果:
在函數執行前
你好!
在函數執行後
說明:
@simple_decorator
將say_hello
函數傳遞給simple_decorator
函數。simple_decorator
在内部生成並返回一個新的wrapper
函數。- 當
say_hello
被呼叫時,執行的是wrapper
而不是原始函數。 wrapper
函數會在内部執行額外的操作(打印消息),然後呼叫原始函數。
3. 在 Django 中編寫自訂裝飾器的原理
在 Django 中編寫裝飾器時,您必須會處理 視圖函數的參數 request
物件。根據此可以對特定條件下的 HTTP 回應進行操作或執行邏輯。
4. 自訂裝飾器編寫的分步結構
以下是 Django 中自訂裝飾器的基本結構:
functools.wraps
使用: 使用@wraps
以保持原始函數的名稱、文檔字符串等元數據。- 基於
request
的邏輯添加: 編寫分析或驗證請求物件的邏輯。 - 呼叫原始函數或返回替代應答: 根據特定條件阻止原始函數的呼叫並返回其他應答。
以下是範例代碼:
from functools import wraps
from django.http import HttpResponseForbidden
def custom_decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
# 在請求物件中確認用戶信息
if not request.user.is_authenticated:
return HttpResponseForbidden("您必須登錄才能訪問此頁面。")
# 呼叫原始視圖函數
return view_func(request, *args, **kwargs)
return _wrapped_view
5. 如何為裝飾器添加參數?
如果裝飾器需要 參數,則必須使用 包裹兩次函數的結構。原因是第一個函數用於處理裝飾器的參數,而第二個函數負責包裹實際的視圖函數。
基本結構:
def decorator_with_params(param1, param2):
def actual_decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
# 利用參數 param1, param2 的邏輯
return view_func(request, *args, **kwargs)
return _wrapped_view
return actual_decorator
說明:
- 第一個函數
decorator_with_params
用於處理 裝飾器的參數(param1
,param2
)。 - 返回內部包裹實際視圖函數的 裝飾器函數(
actual_decorator
)。 - 在
actual_decorator
中呼叫原始視圖函數或執行附加邏輯。
6. 實戰範例:時間限制裝飾器
以下是一個僅在特定時段內可訪問的裝飾器範例。這個裝飾器接收一個參數來處理允許的時間範圍。
from datetime import datetime
from django.http import HttpResponseForbidden
def time_restricted_access(start_hour, end_hour):
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
current_hour = datetime.now().hour
if not (start_hour <= current_hour < end_hour):
return HttpResponseForbidden("此頁面僅在特定時段內可訪問。")
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
@time_restricted_access(9, 17) # 上午9時到下午5時允許訪問
def working_hours_view(request):
return render(request, 'working_hours.html')
運作原理說明:
time_restricted_access
是處理 參數(start_hour
,end_hour
)的 第一個函數。- 第一個函數返回內部包裹視圖的 裝飾器函數。
- 裝飾器函數 接收
request
並檢查當前時間(datetime.now().hour
),根據時間條件:- 如果滿足條件,則呼叫視圖並返回結果。
- 如果不滿足條件,則使用
HttpResponseForbidden
阻止訪問。
為什麼要包裹兩次函數?
之所以包裹兩次函數,是因為 要將參數處理和視圖函數處理的角色分開。
- 第一個函數:接收參數(
start_hour
,end_hour
)並在內部生成裝飾器。 - 第二個函數:生成的裝飾器包裹視圖函數並針對請求執行。
這種結構使裝飾器更加靈活和可重用。
7. 在基於類的視圖(CBV)中使用
基於類的視圖中需要使用 method_decorator
。
from django.utils.decorators import method_decorator
from django.views import View
@method_decorator(time_restricted_access(9, 17), name='dispatch')
class MyView(View):
def get(self, request):
return render(request, 'my_template.html')
8. 結論:編寫自訂裝飾器時的注意事項
- 保持簡單:裝飾器邏輯應簡單明瞭。
- 考慮可重用性:若特定邏輯在多個視圖中使用,請將其提取為裝飾器。
- 處理參數:需要參數時,使用包裹兩次函數的結構。
- 保留元數據:使用
@wraps
保持原始函數的元數據。
總結
現在,您已了解 Django 中自訂裝飾器的內部運作原理,並能編寫接受參數的裝飾器。根據這篇文章編寫符合您項目的裝飾器吧!
Add a New Comment