一、三板斧的原理介紹
1、HttpResponse
在Django中,HttpResponse
是一個類,用於構建HTTP響應並返回給客戶端。當檢視函式處理完請求後,需要返回一個響應時,就會使用HttpResponse
物件。
(1)建立HttpResponse物件
from django.http import HttpResponse
response = HttpResponse(content="Hello, World!", content_type="text/plain", status=200)
(2)引數說明
content
:響應內容,可以是字串、位元組串或檔案物件。content_type
:響應內容的MIME型別。status
:HTTP狀態碼,例如200表示成功,404表示未找到等。- 其他可選引數還包括
charset
、headers
等。
(3)設定響應頭
response['Custom-Header'] = 'Value'
(4)設定Cookie
response.set_cookie('cookie_name', 'cookie_value')
(5)返回Json響應
import json
data = {'key': 'value'}
return HttpResponse(json.dumps(data), content_type="application/json")
HttpResponse
類是Django中處理HTTP響應的基本工具之一,透過使用它,可以方便地構建各種型別的響應並返回給客戶端。
2、render
在Django中,render
是一個常用的快捷函式,用於將資料渲染到指定的模板檔案中,並返回一個包含渲染結果的HttpResponse
物件。
(1)匯入render函式
from django.shortcuts import render
(2)使用方法
def my_view(request):
context = {'key': 'value'}
return render(request, 'template_name.html', context)
(3)引數說明
request
:檢視函式接收到的請求物件。template_name
:要渲染的模板檔案的名稱。context
:包含要傳遞給模板的資料的字典物件。
(4)模板渲染
render
函式會將context
中的資料渲染到指定的模板檔案中。- 在模板檔案中,可以使用模板語言(如Django模板語言)來訪問和顯示傳遞的資料。
(5)返回HttpResponse物件
render
函式返回一個HttpResponse
物件,其中包含了渲染後的HTML內容,可以直接返回給客戶端。
(6)示例
# views.py
from django.shortcuts import render
def my_view(request):
context = {'name': 'Alice', 'age': 30}
return render(request, 'my_template.html', context)
# 當你需要傳的資料特別多的時候可以使用這種方法
# locals()會將所在的名稱空間中所有的名字全部傳遞給html頁面
return render(request, 'my_template.html',locals())
<!-- my_template.html -->
<html>
<head>
<title>Hello, {{ name }}</title>
</head>
<body>
<h1>Hello, {{ name }}</h1>
<p>Age: {{ age }}</p>
</body>
</html>
在上述示例中,render
函式將context
中的資料渲染到my_template.html
模板檔案中,並返回一個包含渲染結果的HttpResponse
物件,最終向客戶端呈現渲染後的頁面內容。render
函式簡化了將資料傳遞給模板並渲染的過程,是Django中常用的檢視函式工具之一。
3、redirect
在Django中,redirect
函式用於重定向使用者到另一個URL。
(1)匯入redirect函式
from django.shortcuts import redirect
(2)使用方法
def my_view(request):
...
return redirect('redirect_url_name')
(3)引數說明
redirect_url_name
:要重定向到的URL的名稱,也可以是URL路徑或檢視函式。
(4)重定向方式
- 當使用者訪問檢視函式時,如果需要將使用者重定向到另一個URL,可以使用
redirect
函式。 - 重定向可以是到另一個檢視函式,也可以是到一個具體的URL路徑。
(5)示例
# views.py
from django.shortcuts import redirect
def my_view(request):
# Some logic here
return redirect('home') # Redirects to a URL named 'home'
(6)重定向到URL路徑
return redirect('/myapp/my_url/') # 重定向到一個特定的url
return redirect('/home/') # 重定向自己的函式不用寫字首
(7)重定向到檢視函式
return redirect(another_view_function) # 重定向到檢視函式
(8)重定向到外部URL
return redirect('https://www.example.com')
(9)重定向時傳遞引數
- 可以在重定向時傳遞引數,例如在URL中包含引數或使用查詢字串。
(10)重定向的特點
- 重定向會向瀏覽器傳送一個302狀態碼,告訴瀏覽器需要重定向到另一個URL。
- 使用者將會看到新的頁面內容,URL也會相應地改變。
redirect
函式在Django中常用於處理使用者請求後的頁面跳轉,使得開發者可以輕鬆地引導使用者到其他頁面或檢視。
4、總結
檢視函式必須要返回第一個HttpResponse物件,研究三者原始碼可得出結論。
render:
Return a HttpResponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments.
redirect:
Return an HttpResponseRedirect to the appropriate URL for the arguments passed.
二、JsonResponse物件
1、json格式的資料有什麼用?
前後端資料互動需要使用到json作為過渡,實現跨語言傳輸資料
前端序列化 python序列化
JSON.stringify() json.dumps()
JSON.parse json.loads()
2、具體案例
- urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('ab_json/', views.ab_json),
]
(1)json模組序列化
def ab_json(request):
user_dict = {
'username': 'xiao好牛逼',
'password': '123',
'hobby': 'read'
}
l = [1, 2, 3, 4, 5]
# 先轉成json格式字串
json_str = json.dumps(user_dict,ensure_ascii=False) # {"username": "xiao", "password": "123", "hobby": "read"}
# 將該字串返回
return HttpResponse(json_str)
(2)JsonResponse物件
問題:使用HttpResponse返回的頁面展示的資料中,中文字元被強制編碼了,如何解決?
讀原始碼掌握JsonResponse用法,修改json_dumps_params引數來防止漢字亂碼。
from django.http import JsonResponse
def ab_json(request):
user_dict = {
'username': 'xiao好牛逼',
'password': '123',
'hobby': 'read'
}
l = [1, 2, 3, 4, 5]
# 先轉成json格式字串
json_str = json.dumps(user_dict,ensure_ascii=False) # {"username": "xiao", "password": "123", "hobby": "read"}
# 讀原始碼掌握JsonResponse用法,修改json_dumps_params引數來防止漢字亂碼
return JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})
(3)其他資料型別序列化
- 列表引數報錯
return JsonResponse(l) # In order to allow non-dict objects to be serialized set the safe parameter to False.
- 看報錯資訊新增safe引數
from django.http import JsonResponse
def ab_json(request):
user_dict = {
'username': 'xiao好牛逼',
'password': '123',
'hobby': 'read'
}
l = [1, 2, 3, 4, 5]
return JsonResponse(l,safe=False) # 預設只能序列化字典,序列化其他需要新增safe引數
JsonResponse在序列化字典以外的資料格式時,會有一個安全設定,我們將引數修改即可修改資料
三、form表單檔案上傳及後端如何獲取
1、form表單知識回顧
<form action="" method="post" enctype="multipart/form-data"></form>
form表單上傳檔案型別的資料
- method必須指定post
- enctype必須換成formdata(不換成這個接收不到檔案)
2、獲取不同資料的方式
urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# form 表單上傳 下載檔案
path('ab_file/', views.ab_file),
]
前端 ab_file.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data" class="form form-control">
<p>username:<input type="text" name="username" class="form-control"></p>
<p>file:<input type="file" name="file" class="form-control"></p>
<input type="submit">
</form>
</body>
</html>
(1)POST請求資料
- csrf記得要註釋掉,不然會報錯
def ab_file(request):
if request.method == 'POST':
# request.POST只能獲取普通的鍵值對資料,獲取檔案不行
print(request.POST)
return render(request,'ab_file.html')
<QueryDict: {'username': ['xiao']}>
(2)獲取檔案資料
def ab_file(request):
if request.method == 'POST':
print(request.FILES) # <MultiValueDict: {'file': [<InMemoryUploadedFile: 聯想截圖_20240124153420.png (image/png)>]}>
file_obj = request.FILES.get('file') # 檔案物件
# print(file_obj.name) # 聯想截圖_20240124153420.png
with open(file_obj.name,'wb') as f:
for line in file_obj.chunks: # 推薦加上chunks方法,其實不加是一樣的,都是一行行的二進位制資料讀取
# print(line) # 二進位制資料
f.write(line)
return render(request,'ab_file.html')
<MultiValueDict: {'file': [<InMemoryUploadedFile: 聯想截圖_20240124153420.png (image/png)>]}>
四、request物件方法擴充
1、request.method
在Django中,request.method
是HttpRequest物件的一個屬性,用於獲取當前請求的HTTP方法。HTTP方法指定了客戶端對伺服器執行的操作型別。常見的HTTP方法包括 GET、POST、PUT、DELETE 等。
(1)獲取請求方法
- 透過
request.method
可以獲取當前請求的HTTP方法。 - 例如,如果請求是透過GET方法傳送的,
request.method
將返回字串'GET'
。
(2)常見的HTTP方法
- GET:用於從伺服器獲取資料。
- POST:用於向伺服器提交資料。
- PUT:用於更新資源。
- DELETE:用於刪除資源。
- PATCH:用於部分更新資源。
- 等等...
(3)使用示例
def my_view(request):
if request.method == 'GET':
# 處理GET請求
pass
elif request.method == 'POST':
# 處理POST請求
pass
(4)注意事項
- 在處理檢視函式時,通常會根據請求的方法型別執行不同的邏輯。
- 可以使用
if
語句或switch
語句根據不同的請求方法執行相應的操作。
透過檢查request.method
,開發者可以根據不同的HTTP方法執行相應的操作,從而實現對請求的靈活處理。
2、request.POST
在Django中,request.POST
是一個類似字典的資料結構,用於訪問使用者透過 POST 方法提交的資料。當使用者透過表單提交資料時,這些資料將包含在 request.POST
中。
(1)獲取 POST 資料
- 當使用者透過 POST 方法提交表單時,表單中的資料將被儲存在
request.POST
中。 - 可以透過鍵來訪問特定的資料項,就像訪問字典一樣,例如
request.POST['key']
。
(2)POST 資料的不可變性
request.POST
是不可變的,這意味著您不能直接修改它的內容。- 如果您需要修改 POST 資料,可以將其複製到一個可修改的資料結構中,如
QueryDict
。
(3)處理表單資料
- 在處理表單資料時,通常會使用
request.POST
來獲取使用者提交的資料,並進行相應的處理,例如驗證、儲存到資料庫等操作。
總的來說,request.POST
在處理使用者透過 POST 方法提交的表單資料時非常有用。
3、request.GET
在Django中,request.GET
是一個類似字典的資料結構,用於訪問使用者透過 GET 方法提交的資料。當使用者透過 URL 查詢字串傳遞資料時,這些資料將包含在 request.GET
中。
(1)獲取 GET 資料
- 當使用者透過 GET 方法傳遞資料時,這些資料將被儲存在
request.GET
中。GET 資料通常以查詢字串的形式附加在 URL 後面,例如http://example.com/?key1=value1&key2=value2
。
(2)安全性考慮
- GET 請求中的資料可以在 URL 中看到,因此不應該用於傳輸敏感資訊,因為這些資料會被儲存在瀏覽器歷史記錄、伺服器日誌等地方。
(3)使用示例
- 開發者可以透過類似字典的方式訪問
request.GET
中的資料,例如request.GET['key']
或request.GET.get('key')
。
(4)區別
- 在處理表單提交時,通常會使用
request.POST
來訪問 POST 資料,而在處理透過 URL 傳遞的引數時,會使用request.GET
來訪問 GET 資料。
4、request.FILES
在Django中,request.FILES
是一個類似字典的資料結構,用於訪問透過表單提交的檔案資料。當使用者透過表單上傳檔案時,這些檔案資料將包含在 request.FILES
中。
(1)處理檔案上傳
- 當使用者透過表單上傳檔案時,這些檔案資料將被儲存在
request.FILES
中。開發者可以透過request.FILES
訪問這些檔案資料,進行處理、儲存或其他操作。
(2)檔案上傳表單
- 在 HTML 表單中,檔案上傳欄位需要使用
<input type="file">
標籤,並且在表單標籤中新增enctype="multipart/form-data"
屬性來指示檔案上傳。
(3)檔案物件
request.FILES
中的每個檔案都是一個類似字典的物件,包含檔案的內容、檔名、大小等資訊。開發者可以透過這些屬性訪問和處理檔案資料。
(4)示例
- 透過
request.FILES['file_field_name']
或request.FILES.get('file_field_name')
可以訪問上傳的檔案資料。
(5)安全性考慮
- 需要謹慎處理使用者上傳的檔案,以防止安全漏洞,例如檔案包含漏洞、檔案上傳漏洞等。
在處理檔案上傳時,開發者通常會結合使用 request.POST
(用於訪問表單中的其他資料)和 request.FILES
(用於訪問上傳的檔案資料)來完整處理表單提交的資料。
5、request.body
在Django中,request.body
是一個包含請求主體資料的位元組字串(原生的瀏覽器發過來的二進位制資料)。當客戶端向伺服器傳送 POST 請求時,請求的主體資料將包含在 request.body
中。
(1)位元組字串
request.body
包含了 POST 請求的原始資料,以位元組字串的形式呈現。開發者可以透過解析這個位元組字串來獲取 POST 請求中的資料。
(2)處理資料
- 開發者可以根據請求的內容型別(如 JSON、XML 等)來解析
request.body
中的資料。通常情況下,需要使用適當的方法(如json.loads()
)來解析請求資料。
(3)獲取 POST 資料
- 在某些情況下,開發者可能需要直接訪問
request.body
來處理 POST 請求中的資料,而不是透過 Django 提供的更高階的請求物件屬性(如request.POST
和request.FILES
)。
(4)注意事項
- 在處理
request.body
中的資料時,開發者需要注意資料的編碼方式(如 UTF-8)以及如何正確解析這些資料,以避免出現編碼問題或安全漏洞。
總的來說,request.body
提供了一種直接訪問 POST 請求主體資料的方式,開發者可以根據需要來處理這些原始資料。
6、request.path
只能獲取到路由地址,無法獲取到引數
request.path
:該屬性表示請求URL中的路徑部分。- 它包含在域名之後,在任何查詢引數之前。
- 例如,如果請求的URL是"http://example.com/foo/bar/",那麼
request.path
將為"/foo/bar/"。
7、request.path_info
只能獲取到路由地址,無法獲取到引數
- 用於表示請求URL的路徑部分,不包括域名和查詢引數。
- 與
request.path
相比,request.path_info
更加原始和未經解析。 - 它保留了URL路徑中的任何編碼、特殊字元或斜槓等資訊。
- 例如,對於以下請求URL:"http://example.com/foo/bar/?page=2",
request.path_info
的值將是 "/foo/bar/"。 - 通常情況下,您可以使用
request.path
來獲取丟棄域名後的路徑,而使用request.path_info
來獲取原始的、未解析的路徑。這在某些情況下非常有用,例如當您需要對URL進行一些自定義操作或路由處理時。
8、request.get_full_path()
即能獲取到路由地址又能獲取到完整的路由地址後面的引數
request.get_full_path()
:該方法返回請求URL的完整路徑,包括路徑部分和任何查詢引數。- 當您需要將完整URL作為字串使用時,這個方法非常有用。
- 例如,如果請求的URL是"http://example.com/foo/bar/?page=2",
request.get_full_path()
將返回"/foo/bar/?page=2"。
五、FBV與CBV
在 Django 中,檢視(Views)是處理 Web 請求並返回 Web 響應的關鍵部分。Django 支援兩種型別的檢視:函式檢視(Function-Based Views,FBV)和基於類的檢視(Class-Based Views,CBV)。
檢視函式既可以是函式也可以是類
1、函式檢視(FBV):
(1)函式檢視的特點
- 使用函式來處理請求和生成響應。
- 使用 Python 函式定義檢視邏輯。
- 相對簡單,適合處理簡單的請求邏輯。
(2)示例
from django.http import HttpResponse
def my_view(request):
# 處理請求邏輯
if request.method == 'GET':
# 處理 GET 請求
return HttpResponse('Hello, GET Request!')
elif request.method == 'POST':
# 處理 POST 請求
return HttpResponse('Hello, POST Request!')
2、基於類的檢視(CBV):
(1)類檢視的特點
- 使用基於類的方式來處理請求和生成響應。
- 提供了更多的程式碼複用和組織結構。
- 能夠直接根據請求方式的不同直接匹配到對應的方法執行
- Django 提供了許多內建的類檢視,可以輕鬆地擴充套件和定製。
(2)示例
CBV路由
path('login/', views.MyLogin.as_view())
views.py
from django.views import View
from django.http import HttpResponse
class MyView(View):
def get(self, request):
return render(request, '02 form.html')
def post(self, request):
return HttpResponse('post方法')
3、如何選擇 FBV 還是 CBV:
-
FBV 適用於:
- 簡單的請求邏輯。
- 較少的程式碼複用需求。
- 對函數語言程式設計更熟悉的開發者。
-
CBV 適用於:
- 複雜的請求邏輯。
- 需要更多程式碼複用和組織結構的情況。
- 對物件導向程式設計更熟悉的開發者。
在實際開發中,可以根據具體需求和個人喜好選擇使用函式檢視還是類檢視來處理請求。 Django 提供了這兩種選擇,使開發者可以根據專案的需求來靈活地選擇合適的檢視型別。
六、CBV原始碼剖析
1、原始碼入口分析
FBV路由
path('login/',views.view)
CBV路由
path('login/',views.MyLogin.as_view())
上述程式碼在啟動Django的時候就會立刻執行as_view方法。
分析:
由於函式名/方法名 加括號執行優先順序最高
所以猜測as_view()
- 要麼是被@staticmethod修飾的靜態方法
- 要麼是被@classmethod修飾的類方法
於是檢視原始碼
as_view()是繫結給類的靜態方法,將類作為第一個引數傳進去
而view 這是一個閉包函式
-
返回值是這個閉包函式的記憶體地址
-
在啟動Django專案時,就會立刻執行as_view方法
- 即
path('login/', views.view()),
得出結論
CBV跟FBV一模一樣,他們在路由匹配本質上是一樣的,都是路由對應函式記憶體地址。
在看原始碼的時候,一定要時刻提醒自己物件導向屬性方法查詢順序
先從物件本身去找
再從產生物件的類中去找
之後再去父類裡面找
...
總結:看原始碼只要看到了self點一個東西,一定要問自己當前這個self到底是誰
2、view 方法剖析
class View:
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
@classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(
'The method name %s is not accepted as a keyword argument '
'to %s().' % (key, cls.__name__)
)
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls是我們自己寫的類
# self = MyLogin(**initkwargs) 產生一個我們自己寫的類的物件
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
update_wrapper(view, cls, updated=())
update_wrapper(view, cls.dispatch, assigned=())
return view
def setup(self, request, *args, **kwargs):
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# CBV 重點 !!!
def dispatch(self, request, *args, **kwargs):
# 獲取當前請求的小寫格式,然後比對當前的請求方式是否合法
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
"""
反射:透過字串來操作物件的屬性或方法
handler = getattr(自己的類產生的物件,'get',當找不到get屬性或者方法的時候就會用第三個引數)
handler = 自己寫的類裡面的get方法
"""
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
# 自動呼叫get方法
def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return HttpResponseNotAllowed(self._allowed_methods())
def options(self, request, *args, **kwargs):
response = HttpResponse()
response.headers['Allow'] = ', '.join(self._allowed_methods())
response.headers['Content-Length'] = '0'
return response
def _allowed_methods(self):
return [m.upper() for m in self.http_method_names if hasattr(self, m)]
3、小結
- 當我們啟動Django專案時,會自動觸發路由中的方法,呼叫 as_view 方法並自動執行
- 在執行後我們檢視 as_view 方法的原始碼 發現
- 在依次給我們的物件賦值後,最終返回了一個自執行的 dispatch 方法
- 於是我們又去檢視了 dispatch 方法
- 在 dispatch 內部 ,先是將請求方式轉換並進行校驗
- 然後開始校驗需要呼叫的方法的呼叫位置,校驗成功並拿到需要執行的方法執行
- 在自己寫的類中如果有相關的方法,會首先呼叫我們重寫的類方法,並返回執行結果
- 如果自己的類裡面沒有該方法 會去自己的父類中呼叫 父類的方法
- 如果父類 以及 基類 都找不到則報錯,丟擲異常
七、給檢視加裝飾器
CBV中django不建議你直接給類的方法新增裝飾器,無論該裝飾器能否正常執行,都不建議直接加
1、使用裝飾器裝飾FBV
- FBV本身就是一個函式,所以和給普通的函式加裝飾器沒有區別:
def wrapper(func):
def inner(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print("used:", end_time-start_time)
return ret
return inner
# FBV版新增班級
@wrapper
def add_class(request):
if request.method == "POST":
class_name = request.POST.get("class_name")
models.Classes.objects.create(name=class_name)
return redirect("/class_list/")
return render(request, "add_class.html")
2、使用裝飾器裝飾CBV
- 類中的方法與獨立函式不完全相同,因此不能直接將函式裝飾器應用於類中的方法 ,我們需要先將其轉換為方法裝飾器。
- Django中提供了method_decorator裝飾器用於將函式裝飾器轉換為方法裝飾器。
# CBV版新增班級
from django.views import View
from django.utils.decorators import method_decorator
def wrapper(func):
def inner(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print("used:", end_time-start_time)
return ret
return inner
@method_decorator(wrapper, name='get') # 方式二(可以新增多個針對不同的方法新增不同的裝飾器)
@method_decorator(wrapper, name='post')
class AddClass(View):
@method_decorator(login_auth) # 方式三:它會直接作用於當前類裡面的所有的方法,但這也是弊端
def dispatch(self, request, *args, **kwargs):
pass
@method_decorator(wrapper) # 方式一:指名道姓
def get(self, request):
return render(request, "add_class.html")
def post(self, request):
class_name = request.POST.get("class_name")
models.Classes.objects.create(name=class_name)
return redirect("/class_list/")
3、CBV的擴充
- 使用CBV時要注意,請求過來後會先執行dispatch()這個方法
- 如果需要批次對具體的請求處理方法,如get,post等做一些操作的時候,這裡我們可以手動改寫dispatch方法,這個dispatch方法就和在FBV上加裝飾器的效果一樣。
class Login(View):
def dispatch(self, request, *args, **kwargs):
print('before')
obj = super(Login,self).dispatch(request, *args, **kwargs)
print('after')
return obj
def get(self,request):
return render(request,'login.html')
def post(self,request):
print(request.POST.get('user'))
return HttpResponse('Login.post')