一、Django 請求周期生命流程圖
首先,用戶在瀏覽器中輸入URL,發送一個GET 或 POST 方法的request 請求,
Django 中封裝了socket 的WSGI 服務器,監聽埠接受這個request 請求,
再進行初步封裝,然后傳送到中間件中,這個request 請求再依次經過中間件,
對請求進行校驗或處理,再傳輸到路由系統中進行路由分發,匹配相對應的視圖函式(FBV),
再將request 請求傳輸到views 中的這個視圖函式中,進行業務邏輯的處理,
呼叫modles 中表物件,通過ORM 拿到資料庫(DB)的資料,
同時拿到templates 中相應的模板進行渲染,然后將這個封裝了模板response 回應傳輸到中間件中,
依次進行處理,最后通過WSGI 再進行封裝處理,回應給瀏覽器展示給用戶,
二、Django 路由配置
路由簡單的來說就是根據用戶請求的 URL 連接來判斷對應的處理程式,并回傳處理結果,也就是 URL 與 Django 的視圖建立映射關系,
Django 路由在 urls.py 配置,urls.py 中的每一條配置對應相應的處理方法,
urls.py 檔案
from django.conf.urls import url
# 由一條條映射關系組成的urlpatterns這個串列稱之為路由表
urlpatterns = [
url(regex, view, kwargs=None, name=None), # url本質就是一個函式
]
#函式url關鍵引數介紹
# regex:正則運算式,用來匹配url地址的路徑部分,
# 例如url地址為:http://127.0.0.1:8001/index/,正則運算式要匹配的部分是index/
# view:通常為一個視圖函式,用來處理業務邏輯
# kwargs:略(用法詳見有名分組)
# name:略(用法詳見反向決議)
案例:
urls.py 檔案
from django.conf.urls import url
from django.contrib import admin
from app import views # 匯入模塊views.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/$', views.index),
]
views.py 檔案
# 匯入HttpResponse,用來生成回應資訊
from django.shortcuts import render, HttpResponse
# 新增視圖函式index
def index(request):
return HttpResponse('index page')
測驗
python manage.py runserver # 在瀏覽器輸入:http://127.0.0.1:8000/index/ 會看到 index page
1 注意一
在瀏覽器輸入:http: //127.0.0.1:8001/index/,Django 會拿著路徑部分 index/ 去路由表(urls.py檔案)中自上而下匹配正則運算式,一旦匹配成功,則會立即執行該路徑對應的視圖函式,也就是上面路由表(urls.py檔案)中的 uelspatterns 串列中的url('^index/$',views.index) 也就是views.py 視圖函式檔案的index函式,
2 注意二
在瀏覽器中輸入:http: //127.0.0.1:8001/index,Django 同樣會拿著路徑部分 index 去路由表中自上而下匹配正則運算式,看起來好像是匹配不到正則運算式(r'^index/$' 匹配的是必須以/結尾,所以必會匹配到成功index),但是實際上我們依然在瀏覽器寬口中看到了 ‘index page’,其原因如下:
在組態檔 settings.py 中有一個引數 APPEND_SLASH,該引數有連個值True/False,
當APPEND_SLASH = True(如果組態檔中沒有該配置,則默認值為 True),并且用戶請求的 URL 地址的路徑部分不是以 / 結尾
例如請求的 URL 地址為:http: //127.0.0.1:8001/index,Django也會拿著部分地址 index 去路由表中匹配正則運算式,發現匹配不成功,那么Django 會在路徑后加/(index/)在去路由表中匹配,去過還匹配不到,會回傳路徑找不到,如果匹配成功,則會回傳重定向資訊給瀏覽器,要求瀏覽器重新向 http: //127.0.0.1:8001/index/ 地址返送請求,
當APPEND_SLASH = False時,則不會執行上述程序,即以但 URL 地址的路徑部分匹配失敗就立即回傳路勁未找到,不會做任何的附加操作,
# settings.py
APPEND_SLASH = False

三、路由分組
1 無名分組
urls.py 檔案
from django.conf.urls import url
from django.contrib import admin
from app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 下述正則運算式會匹配url地址的路徑部分為:article/數字/
# 匹配成功的分組部分會以位置引數的形式傳給視圖函式,有幾個分組就傳幾個位置引數
url(r'^aritcle/(\d+)/$', views.article),
]
views.py 檔案
from django.shortcuts import render, HttpResponse
# 需要額外增加一個形參用于接收傳遞過來的分組資料
def article(request,article_id):
return HttpResponse('id為 %s 的文章內容...' %article_id)
測驗
python manage.py runserver
在瀏覽器輸入: http://127.0.0.1:8000/article/1/ 會看到: id為 1 的文章內容...
2 有名分組
urls.py 檔案
from django.conf.urls import url
from django.contrib import admin
from app import views
urlpatterns = [
url(r'^admin',admin.site.urls),
# 下面的正則運算式會匹配url地址的路徑部分為:article/數字/
# 匹配成功的分組部分會以 關鍵字引數(article_id=..)的形式傳給視圖函式
# 有幾個分組就傳幾個關鍵字引數
# (\d+)代表匹配數字1-無窮個
url(r'aritcle/(?P<article_id>\d+)/$', view.article),
]
views.py 檔案
from django.shortcuts import render, HttpResponse
# 需要額外增加一個形參,形參名必須為article_id
def article(request, article_id):
return HttpResponse('id為 %s 的文章內容...' %article_id)
測驗
python manage.py runserver
在瀏覽器輸入:http://127.0.0.1:8000/article/1/ 會看到: id為 1 的文章內容...
總結:有名分組和無名分組都是為了獲取路徑中的引數,并傳遞給視圖函式,區別在于無名分組是以位置引數的形式傳遞,有名分組是以關鍵字引數的形式傳遞,
強調:無名分組和有名分組不要混合使用,
四、路由分發
隨著專案功能的增加,app會越來越多,路由也越來越多,每個app都會有屬于自己的路由,如果再將所有的路由都放到一張路由表中,會導致結構不清晰,不便于管理,所以我們應該將app 自己的路由交由自己管理,然后在總路由表中做分發,
1 創建兩個 app,記得注冊
# 新建專案mystie
G:\src\django>django-admin startproject mysite
# 切換到專案目錄下
G:\src\django>cd mysite
# 創建app01和app02
G:\src\django\mysite>python manage.py startapp app01
G:\src\django\mysite>python manage.py startapp app02
2 在每個app下手動創建urls.py 來存放自己的路由
app01 下的urls.py 檔案
from django.conf.urls import url
# 匯入app01 的views
from app01 import views
urlpatterns = [
url(r'^index/$',views.index),
]
app01 下的views.py檔案
from django.shortcuts import render, HttpResponse
def index(request):
return HttpResponse('我是app01 的index頁面...')
app02下 的urls.py檔案
from django.conf.urls import url
# 匯入app02的views
from app02 import views
urlpatterns = [
url(r'^index/$',views.index),
]
app02 下的views.py檔案
from django.shortcuts import render, HttpResponse
def index(request):
return HttpResponse('我是app02 的index頁面...')
3 在總路由表的 urls.py 檔案中(mysite檔案夾下的 urls.py)
注意:總路由中,一級路由的后面千萬不加$符號,不然不能進行分發路由的操作,表示結束匹配,
from django.conf.urls import url, include
from django.contrib import admin
# 總路由表
# from app01 import urls as app01_urls
# from app02 import urls as app02_urls
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 1.路由分發
# url(r'^app01/',include(app01_urls)), # 只要url前綴是app01開頭 全部交給app01處理
# url(r'^app02/',include(app02_urls)) # 只要url前綴是app02開頭 全部交給app02處理
# 新增兩條路由,注意不能以$結尾
# include 函式就是做分發操作的,當在瀏覽器輸入 http://127.0.0.1:8001/app01/index/ 時
# 會先進入到總路由表中進行匹配,正則運算式 r'^app01/' 會先匹配成功路徑app01/
# 然后 include 功能會去 app01 下的urls.py 中繼續匹配剩余的路徑部分
# 推薦使用
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls')),
]
4 測驗
python manage.py runserver
在瀏覽器輸入: http://127.0.0.1:8000/app01/index/ 會看到:我是app01 的index頁面...
在瀏覽器輸入: http://127.0.0.1:8000/app02/index/ 會看到:我是app02 的index頁面...
五、反向決議
在軟體開發初期,URL 地址的路徑設計可能并不完美,后期需要進行調整,如果專案中很多地方使用了該路徑,一旦該路徑發生變化,就意味著所有使用該路徑的地方都需要進行修改,這是一個非常繁瑣的操作,
解決方案就是在撰寫一條 url(regex, view, kwargs=None, name=None) 時,可以通過引數name為 URL 地址的路徑部分起一個別名,專案中就可以通過別名來獲取這個路徑,以后無論路徑如何變化別名與路徑始終保持一致,
上述方案中通過別名獲取路徑的程序稱為反向決議,
案例:登錄成功跳轉到 index.html 頁面,
1 在mysite/urls.py檔案
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/$', views.login, name='login_page'), # 路徑login/的別名為login_page
url(r'^index/$', views.index, name='index_page'), # 路徑index/的別名為index_page
]
2 在app01/views.py 檔案
from django.shortcuts import render, HttpResponse, redirect, reverse # 用于反向決議
def login(request):
if request.method == 'GET':
# 當為get 請求時,回傳login.html頁面,頁面中的 {% url 'login_page' %} 會被反向決議成路徑:/login/
return render(request, 'login.html')
# 當為post 請求時,可以從 request.POST 中取出請求體的資料
name = request.POST.get('name')
pwd = request.POST.get('pwd')
if name == 'xyz' and pwd == '123':
url = reverse('index_page') # reverse 會將別名 'index_page' 反向決議成路徑:/index/
return redirect(url) # 重定向到/index/
else:
return HttpResponse('用戶名或密碼錯誤')
def index(request):
return render(request, 'index.html')
3 templates\login.html 檔案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<!--強調: login_page 必須加引號-->
<form action="{% url 'login_page' %}" method="post">
<p>用戶名: <input type="text" name="name"></p>
<p>密 碼: <input type="password" name="pwd"></p>
<p><input type="submit" value="https://www.cnblogs.com/huaxiayuyi/archive/2022/09/26/提交"></p>
</form>
</body>
</html>
4 templates\index.html 檔案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body>
<h3>index page</h3>
</body>
</html>
5 測驗
python manage.py runserver
在瀏覽器輸入: http://127.0.0.1:8000/login/ 會看到登錄頁面,輸入正確的用戶名密碼會跳轉到 index.html
當我們修改路由表中匹配路徑的正則運算式時,程式其余部分均無需修改
6 總結
- 在 views.py 檔案中,反向決議的使用:url = reverse('index_page')
- 在模版 login.html 檔案中,反向決議的使用:{% url 'login_page' %}
7 如果路徑存在分組的反向決議使用
from django.shortcuts import HttpResponse, render, reverse
from django.conf.urls import url
from django.contrib import admin
def index(request, args):
return HttpResponse('index page')
def user(request, uid):
return HttpResponse('user page')
def home(request):
# 無名
index = reverse('index_page', args=(1,))
# 有名
user = reverse('user_page', kwargs={'uid': 12})
# 簡寫
# user = reverse('user_page', args=(12,))
return render(request, 'home.html', locals())
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', home), # 首頁
url(r'^index/(\d+)/$', index, name='index_page'), # 無名分組
url(r'^user/(?P<uid>\d+)/$', user, name='user_page'), # 有名分組
]
8 templates\home.html 檔案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>home</title>
</head>
<body>
<h3>home page</h3>
<p>{{index}}</p>
<p>{{user}}</p>
<p>{% url 'index_page' 1%}</p>
<p>{% url 'user_page' 12%}</p>
<p>{% url 'user_page' uid=1 %}</p>
</body>
</html>
無名分組
反向決議出:/index/1/ 這種路徑,寫法如下在 views.py 中,
反向決議的使用
url = reverse('index_page', args=(1,))
在模版 login.html 檔案中,反向決議的使用(1 是匹配\d+)
{% url 'index_page' 1 %}
有名分組
反向決議出:/user/1/ 這種路徑,寫法如下在 views.py 中,
反向決議的使用
url = reverse('user_page', kwargs={'uid':1})
在模版 login.html 檔案中,反向決議的使用
{% url 'user_page' uid=1 %}
六、名稱空間
當我們的專案下創建了多個app,并且每個app下都針對匹配的路徑起了別名,如果別名存在重復,那么在反向決議時則會出現覆寫
1 在每個app下手動創建urls.py 來存放自己的路由,并且為匹配的路徑起別名 app01 下的urls.py 檔案from django.conf.urls import url
from app01 import views
urlpatterns = [
# 為匹配的路徑 app01/index/ 起別名 'index_page'
url(r'^index/$', views.index, name='index_page'),
]
app02 下的urls.py 檔案
from django.conf.urls import url
from app02 import views
urlpatterns = [
# 為匹配的路徑 app02/index/ 起別名 'index_page',與app01 中的別名相同
url(r'^index/$', views.index, name='index_page'),
]
2 在每個app下的view.py 中撰寫視圖函式,在視圖函式中針對別名 'index_page' 做反向決議
app01 下的 views.py 檔案
from django.shortcuts import render, HttpResponse, reverse
def index(request):
url = reverse('index_page')
return HttpResponse('app01 的index頁面,反向決議結果為%s' %url)
app02 下的views.py 檔案
from django.shortcuts import render, HttpResponse, reverse
def index(request):
url = reverse('index_page')
return HttpResponse('app02 的index頁面,反向決議結果為%s' %url)
3 在總的urls.py 檔案中(mysite 檔案夾下的 urls.py)
from django.conf.urls import url, include
from django.contrib import admin
# 總路由表
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 新增兩條路由,注意不能以$結尾
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls')),
]
測驗
python manage.py runserver
在測驗時,無論在瀏覽器輸入:http://127.0.0.1:8001/app01/index/還是輸入http://127.0.0.1:8001/app02/index/ 針對別名 'index_page' 反向決議的結果都是 /app02/index/,覆寫了app01下別名的決議,
1 總urls.py 在路由分發時,指定名稱空間
from django.conf.urls import url, include
from django.contrib import admin
# 總路由表
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 傳給include功能一個元組,元組的第一個值是路由分發的地址,第二個值則是我們為名稱空間起的名字
# url(r'^app01/', include(('app01.urls', 'app01'))),
# url(r'^app02/', include(('app02.urls', 'app02'))),
url(r'^app01/', include(('app01.urls', 'app01'), namespace='app01')),
url(r'^app02/', include(('app02.urls', 'app02'), namespace='app02'))
]
2 修改每個app下的view.py 中視圖函式,針對不同名稱空間中的別名 'index_page' 做反向決議
app01 下的views.py 檔案
from django.shortcuts import HttpResponse
def index(request):
# 決議的是名稱空間app01 下的別名 'index_page'
url = reverse('app01:index_page')
return HttpResponse('app01 的index頁面,反向決議結果為%s' %url)
app02 下的views.py 檔案
from django.shortcuts import HttpResponse
def index(request):
# 決議的是名稱空間app02下的別名'index_page'
url = reverse('app02:index_page')
return HttpResponse('app02 的index頁面,反向決議結果為%s' %url)
3 測驗
python manage.py runserver
瀏覽器輸入: http://127.0.0.1:8000/app01/index/ 反向決議的結果是 /app01/index/
瀏覽器輸入: http://127.0.0.1:8000/app02/index/ 反向決議的結果是 /app02/index/
總結 + 補充
# 在視圖函式中基于名稱空間的反向決議,用法如下
url = reverse('名稱空間的名字:待決議的別名')
# 在模版里基于名稱空間的反向決議,用法如下
<a href="https://www.cnblogs.com/huaxiayuyi/archive/2022/09/26/{% url'名稱空間的名字:待決議的別名'%}">index page</a>
# 其實只要保證名字不沖突 就沒有必要使用名稱空間
4 namespace 引數
在根目錄下的 urls.py 中使用了 include 方法,并且使用了 namespace 引數,如下圖

在啟動專案時,會報錯:'Specifying a namespace in include() without providing an app_name '
這是因為Django2 相對于Django1 做了改動,在include 函式里增加了引數 app_name,表示app的名字,
解決方法:
在 include 中傳入該app的名字(第二個引數),即
七、Django2.0 版的re_path與path
1 re_path
Django2.0 中的re_path與Django1.0 的URL一樣,傳入的第一個引數都是正則運算式
from django.urls import re_path # Django3.2 中的re_path
from django.conf.urls import url # Django3.2 中同樣可以匯入1.0中的url
urlpatterns = [
# 用法完全一致
url(r'^app01/', include(('app01.urls','app01'))),
re_path(r'^app02/', include(('app02.urls','app02'))),
]
2 path
在Django2.0 中新增了一個path功能,用來解決:資料型別轉換問題與正則運算式冗余問題
from django.shortcuts import HttpResponse,
from django.urls import path, re_path
urlpatterns = [
# 雖然path 不支持正則 但是它的內部支持五種轉換器
# 將第二個路由里面的內容先轉成整型然后以 關鍵字 的形式傳遞給后面的視圖函式
path('index/<int:id>/', index)
]
# id 關鍵字引數
def index(request, id):
print(id, type(id))
return HttpResponse('index page')
強調
- path與re_path或者1.0中的url的不同之處是,傳給path的第一個引數不再是正則運算式,而是一個完全匹配的路徑,相同之處是第一個引數中的匹配字符均無需加前導斜杠,
- 使用尖括號(<>)從url中捕獲值,相當于有名分組,
- <>中可以包含一個轉化器型別(converter type),比如使用 <int:name> 使用了轉換器int若果沒有轉化器,將匹配任何字串,當然也包括了 / 字符,
- str 匹配除了路徑分隔符(/)之外的非空字串,這是默認的形式0,
- int 匹配正整數,包含0,
- slug 匹配字母、數字以及橫杠、下劃線組成的字串,
- uuid 匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00,
- path 匹配任何非空字串,包含了路徑分隔符(/)(不能用?)0,
例如
path('articles/<int:year>/<int:month>/<slug:other>/', views.article_detail)
針對路徑 http://127.0.0.1:8000/articles/2009/123/info/
path 會匹配出引數 year=2009,month=123,other='info' 傳遞給函式 article_detail,
很明顯針對月份 month,轉換器int是無法精準匹配的,如果我們只想匹配兩個字符,那么轉換器slug也無法滿足需求,針對等等這一系列復雜的需要,我們可以定義自己的轉化器,轉化器是一個類或介面,它的要求有三點:
- regex 類屬性,字串型別,
- to_python(self, value) 方法,value是由類屬性 regex 所匹配到的字串,回傳具體的Python 變數值,以供Django 傳遞到對應的視圖函式中,
- to_url(self, value) 方法,和 to_python 相反,value是一個具體的Python 變數值,回傳其字串,通常用于 URL 反向參考,
自定義轉換器示例
在app01 下新建檔案 path_ converters.py,檔案名可以隨意命名
class MonthConverter:
regex = '\d{2}' # 屬性名必須為regex
def to_python(self, value):
return int(value)
def to_url(self, value):
return value # 匹配的regex是兩個數字,回傳的結果也必須是兩個數字
在urls.py中,使用 register_converter 將其注冊到 URL 配置中
from django.urls import path,register_converter
from app01.path_converts import MonthConverter
register_converter(MonthConverter, 'mon')
from app01 import views
urlpatterns = [
path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='xxx'),
]
views.py 檔案中的視圖函式 article_detail
from django.shortcuts import HttpResponse
def article_detail(request,year,month,other):
print(year, type(year))
print(month, type(month))
print(other, type(other))
print(reverse('xxx', args=(1988, 12, 'info'))) # 反向決議結果/articles/1988/12/info/
return HttpResponse('article detail page')
測驗
- 在瀏覽器輸入 http://127.0.0.1:8000/articles/2009/12/info/ path 會成功匹配出引數 year=2009,month=12(month必須兩位數),other='info' 傳遞給函式 article_detail,
- 在瀏覽器輸入 http://127.0.0.1:8000/articles/2009/123/info/ path 會匹配失敗,因為我們自定義的轉換器mon只匹配兩位數字,而對應位置的123超過了2位,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/509578.html
標籤:其他
