一、MVC和MTV框架
MVC
- M 代表模型(Model)
- V 代表檢視(View)
- C 代表控制器(Controller)
Web伺服器開發領域裡著名的MVC模式,所謂MVC就是把Web應用分為模型(M),控制器(C)和檢視(V)三層,他們之間以一種外掛式的、鬆耦合的方式連線在一起,模型負責業務物件與資料庫的對映(ORM),檢視負責與使用者的互動(頁面),控制器接受使用者的輸入呼叫模型和檢視完成使用者的請求。
MTV
Django 的 MTV 模式本質上和MVC是一樣的,也是為了各元件間保持鬆耦合關係,只是定義上有些許不同,Django 的MTV分別是值:
- M 代表模型(Model): 負責業務物件和資料庫的關係對映(ORM)。
- T 代表模板 (Template):負責如何把頁面展示給使用者(html)。
- V 代表檢視(View): 負責業務邏輯,並在適當時候呼叫Model和Template。
除了以上三層之外,還需要一個URL分發器,它的作用是將一個個URL的頁面請求分發給不同的View處理,View 再呼叫相應的 Model 和 Template
二、URL配置
URL配置:它的本質是URL與要為該URL呼叫的檢視函式之間的對映表。
Django 1.x版本
url()方法:普通路徑和正則路徑均可使用,需要自己手動新增正則首位限制符號。
注意:
- urlpatterns中的元素按照書寫順序從上往下逐一匹配,一旦匹配成功則不在繼續
- 若要從URL中捕獲一個值,只需要在它周圍放置一對圓括號(分組匹配)。
# urls.py檔案
from django.contrib import admin
from django.conf.urls import url # 2.x之後,用 url 需要引用
from app01 import views # 需要自行匯入檢視檔案 views.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index), # 新增一條
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render
# 新增檢視函式
def index(request): # request 中包含請求資訊
return render(request, 'index.html') # 返回的 HTML 頁面
Django 2.x之後版本
path:用於普通路徑,不需要自己手動新增正則首位限制符號,底層已經新增。
re_path:用於正則路徑,需要自己手動新增正則首位限制符號。
# urls.py檔案
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index), # 新增-普通路徑
re_path(r'^books/\d{4}/$', views.books) # 新增-正則路徑
]
# -------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, HttpResponse # 匯入HttpResponse,用來生成響應資訊
def index(request):
return render(request, 'index.html') # 返回的 HTML 頁面
def books(request):
return HttpResponse('Hello world') # 響應資訊
三、分組
分組就是需要直接從路徑中取出引數,這就用到了正規表示式的分組功能了。
分組分為兩種:無名分組與有名分組
無名分組
無名分組按照位置傳參,需要一 一對應。
捕獲URL中的值並以位置引數形式傳遞給檢視。
views 中除了request,其他形參的數量要與urls中的分組數量一致。
# ruls.py檔案
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^books/(\d{4})/', views.books),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, HttpResponse
def books(request, year):
print(year) # 一個形參代表路徑中一個分組的內容,按順序匹配
return HttpResponse(f'Hello world {year}')
有名分組
語法:
(?P<組名>正規表示式)
捕獲URL中的值並以關鍵字引數形式傳遞給檢視。
有幾個有名分組就要有幾個關鍵字引數
# urls.py檔案
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
# 匹配成功的分組部分會以關鍵字引數(name_id = 匹配成功的數字)的形式傳給檢視函式。
re_path(r'^books/(?P<name_id>\d{2})/', views.books),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, HttpResponse
# 需要額外增加一個形參,形參名必須為 name_id
def books(request, name_id):
return HttpResponse(f'Hello world {name_id}')
總結:有名分組和無名分組都是為了獲取路徑中的引數,並傳遞給檢視函式,區別在於無名分組是以位置引數的形式傳遞,有名分組是以關鍵字引數的形式傳遞。
四、路由分發(include)
存在的問題:Django專案裡有多個app共用一個 urls 容易造成混淆,後期維護不方便。
解決:使用路由分發(include),讓每個app目錄都單獨擁有自己的 urls。
步驟:
1、在各自 app 目錄下的建立 urls.py檔案, 並對 urls.py 檔案和 views.py 檔案中寫各自的路由和檢視函式。
app01下的 urls.py 檔案和 views.py 檔案
# urls.py檔案
from django.urls import path, re_path
from app01 import views # 匯入app01的views
urlpatterns = [
re_path(r'^index/', views.index),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, HttpResponse
def index(request):
return HttpResponse('app01頁面')
app02下的 urls.py 檔案和 views.py 檔案
# urls.py檔案
from django.urls import path, re_path
from app02 import views # 匯入app02的views
urlpatterns = [
re_path(r'^index/', views.index),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, HttpResponse
def index(request):
return HttpResponse('app02頁面')
2、在專案名稱目錄下的 urls 檔案裡面,統一將路徑分發給各個 app 目錄。
from django.contrib import admin
from django.urls import path, re_path, include
# 總路由表
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
# 新增兩條路由,不能以$結尾
# include函式就是做分發操作的。
re_path(r'^app01/', include('app01.urls')),
re_path(r'^app02/', include('app02.urls')),
]
五、反向解析
如果路由 層的 url 發生變化,就需要取更改對應的檢視層和模板層的 url ,非常麻煩,不便於維護。
所以可以利用反向解析,當路由層 url 發生變化,在檢視層和模組層動態反向解析出更改後的 url ,避免修改操作
反向解析一般用在模板中的超連結以及檢視中的重定向。
普通反向解析
登入成功跳轉到 index.html 頁面:如果 urls.py 檔案中的 index 路徑有所變化,views.py 檔案中不需要更改index路徑。
# urls.py檔案
from django.urls import path, re_path
from app01 import views
urlpatterns = [
# 路徑 login/的別名為login_page
re_path(r'^login/', views.login, name='login_page'),
# 路徑 index/的別名為index_page
re_path(r'^index/', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, reverse, redirect, HttpResponse
def login(request):
# 當為GET請求時就返回登入頁面
if request.method == 'GET':
return render(request, 'login.html')
else:
# post 請求時,就提取出請求資料。
user = request.POST.get('username')
pwd = request.POST.get('password')
if user == 'xiaoyang' and pwd == '123':
# 會對別名反向解析成路徑/index/
url = reverse('index_page')
# 然後哦重定向到別名解析的路徑
return redirect(url)
else:
return HttpResponse('登入失敗')
def index(request):
return HttpResponse('登入成功!!!')
登入成功 HTML頁面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登入頁面</h1>
<!-- 這裡的 "{% url 'login_page' %}" 相當於路徑/login/-->
<form action="{% url 'login_page' %}" method="post">
{% csrf_token %} <!-- post提交需要做CSRF驗證 -->
使用者名稱:<input type="text" name="username">
密碼:<input type="password" name="password">
<input type="submit">
</form>
</body>
</html>
分組反向解析
如果路徑存在分組的反向解析使用:
無名分組:reverse ( "路由別名", arges = ( 符合正則匹配的引數 ) )
有名分組:reverse ( "路由別名", arges = { "分組名":"符合正則匹配的引數" } )
# urls.py檔案
from django.urls import path, re_path
from app01 import views
urlpatterns = [
# 無名分組
re_path(r'^books/(\d{2})/', views.books, name='books_page'),
# 有名分組
re_path(r'^years/(?P<year>\d{4})/', views.years, name='years_page'),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, reverse, redirect, HttpResponse
# 有名分組的反向解析
def years(request, year):
url = reverse('years_page', kwargs={'year': year})
# HTML 檔案中:{% url 'years_page' 'year'=1234 %}
return HttpResponse(url)
# 無名分組的反向解析
def books(request, ret):
url = reverse('books_page', args=(ret,))
# HTML 檔案中:{% url 'books_page' 34 %}
return HttpResponse(url)
六、名稱空間
Django專案裡有多個app,當在不同的 app 目錄下的 urls.py 檔案中定義了相同的路由別名 name 時,那麼在反向解析時則會出現覆蓋。
例如:
不管輸入:http://127.0.0.1:8000/app01/index/ 還是 http://127.0.0.1:8000/app02/index/ 都得到的是 /app02/index/ app02路徑的別名覆蓋了app01 路徑的別名
app01下的 urls.py 檔案和 views.py 檔案
# urls.py檔案
from django.urls import path, re_path
from app01 import views
urlpatterns = [
re_path(r'^index/', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, reverse, HttpResponse
def index(request):
url = reverse('index_page')
return HttpResponse(url)
app02下的 urls.py 檔案和 views.py 檔案
# urls.py檔案
from django.urls import path, re_path
from app02 import views
urlpatterns = [
re_path(r'^index/', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, reverse, HttpResponse
def index(request):
url = reverse('index_page')
return HttpResponse(url)
專案名稱目錄下的 urls 檔案裡面,統一將路徑分發給各個 app 目錄。
# urls.py檔案
from django.urls import path, re_path, include
# 總路由表
urlpatterns = [
re_path(r'^app01/', include('app01.urls')),
re_path(r'^app02/', include('app02.urls')),
]
對於這種問題的解決方法就是避免使用相同的別名,如果要使用相同的別名,那就需要將別名放到不同的名稱空間中去,這樣就避免了即使出現了重複,彼此也不會衝突。
解決方法:
格式:
# 檢視中的名稱空間的方向解析
url=reverse('名稱空間的名字:待解析的別名')
# 模板中的名稱空間的反向解析
<a href="{% url '名稱空間的名字:待解析的別名'%}">小楊</a>
1、在 urls.py 路由分發時指定名稱空間
專案名稱目錄下的 urls 檔案裡面,統一將路徑分發給各個 app 目錄。
# url.py檔案
from django.urls import path, re_path, include
# 總路由表
urlpatterns = [
# 給include傳遞一個元組,第一個是路由分發的地址,第二個是我們自定義的名稱空間名字
re_path(r'^app01/', include(('app01.urls', 'app01'))),
re_path(r'^app02/', include(('app02.urls', 'app02'))),
]
2、修改每個app下的view.py中檢視函式,對不同名稱空間的別名做反向解析
app01下的 urls.py 檔案和 views.py 檔案
# urls.py檔案
from django.urls import path, re_path
from app01 import views
urlpatterns = [
re_path(r'^index/', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, HttpResponse, reverse
def index(request):
# 解析的是app01下的別名‘index_page’
url = reverse('app01:index_page')
return HttpResponse(url)
app02下的 urls.py 檔案和 views.py 檔案
# urls.py檔案
from django.urls import path, re_path
from app02 import views
urlpatterns = [
re_path(r'^index', views.index, name='index_page'),
]
# ------------------------------------------------------------------
# views.py檔案
from django.shortcuts import render, reverse, HttpResponse
def index(request):
# 解析的是app02下的別名‘index_page’
url = reverse('app02:index_page')
return HttpResponse(url)