Django的路由層(URLconf)

  URL配置(URLconf)就像Django 所支撐網站的目錄。它的本質是URL與要爲該URL調用的視圖函數之間的映射表;你就是以這種方式告訴Django,對於客戶端發來的某個URL調用哪一段邏輯代碼對應執行。html

urlpatterns = [
    url(正則表達式, views視圖函數,參數,別名),
]

  參數說明:python

  • 一個正則表達式字符串
  • 一個可調用對象,一般爲一個視圖函數或一個指定視圖函數路徑的字符串
  • 可選的要傳遞給視圖函數的默認參數(字典形式)
  • 一個可選的別名(name)參數
view:
當正則表達式匹配到某個條目時,自動將封裝的HttpRequest對象做爲第一個參數,正則表達式「捕獲」到的值做爲第二個參數,傳遞給該條目指定的視圖。
若是是簡單捕獲,那麼捕獲值將做爲一個位置參數進行傳遞,若是是命名捕獲,那麼將做爲關鍵字參數進行傳遞。 kwargs: 任意數量的關鍵字參數能夠做爲一個字典傳遞給目標視圖。 name: 對你的URL進行命名,可讓你可以在Django的任意處,尤爲是模板內顯式地引用它。至關於給URL取了個全局變量名,你只須要修改這個全局變量的值,
在整個Django中引用它的地方也將一樣得到改變。這是極爲古老、樸素和有用的設計思想,並且這種思想無處不在。

 

1、簡單的路由配置

一、在/first_pro/first_pro/urls.py中進行路由配置

from django.contrib import admin
from django.urls import path,re_path, include

from app01 import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('timer/', views.timer),

    path('login/', views.login),

    # 路由配置:  路徑--------->視圖函數
    # 正則匹配:r'^articles/2003/$'  以articles/2003/開頭且以articles/2003/結尾
    re_path(r'^articles/2003/$', views.special_case_2003),  # special_case_2003(request)

    # 正則匹配四位的任意數字,多對一
    re_path(r'^articles/(/[0-9]{4})/$', views.year_archive),  # year_archive(request,1999)

    # 正則匹配一個四位的數字做爲年,還要匹配一個2位的數字做爲月
    # re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),  # month_archive(request,2009,12)

    # 改寫爲有名分組
    re_path(r'^articles/(?P<y>[0-9]{4})/(?P<m>[0-9]{2})/$', views.month_archive)  # month_archive(request,y=2009,m=12)

    # ([0-9]+):匹配前面的數字一次或無限次做爲詳情
    # re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail)
]

注意:正則表達式

  • 若要從URL中捕獲一個值,值須要在它周圍放置一對圓括號。django

  • 不須要添加一個前導的反斜槓,由於每一個URL都有。例如,應該是^articles而不是^/articles。瀏覽器

  • 每一個正則表達式前面的'r'是可選的可是建議加上。它告訴python這個字符串是「原始的」—— 字符串中任何字符都不該該轉義bash

二、/first_pro/app01/views.py編寫對應的視圖函數

from django.shortcuts import render,HttpResponse

def timer(request):
    import time
    ctime = time.strftime('%Y-%m-%d %H:%M:%S')   # 2018-06-30 05:48:11
    """
    render方法:
        一、幫忙找到timer.html取出裏面的數據;
        二、按照固定的語法({})把變量嵌套到html文件中
    """
    return render(request, 'timer.html', {"date":ctime})    # 使用render方法,返回一個頁面


def special_case_2003(request):
    return HttpResponse("special_case_2003")   # HttpResponse:響應對象


def year_archive(request,year):
    return HttpResponse(year)

def month_archive(request,y,m):
    return HttpResponse(y+"-"+m)

三、訪問調用示例

'''
一些請求的例子:

/articles/2005/03/ 請求將匹配列表中的第三個模式。Django 將調用函數views.month_archive(request, '2005', '03')。
/articles/2005/3/ 不匹配任何URL 模式,由於列表中的第三個模式要求月份應該是兩個數字。
/articles/2003/ 將匹配列表中的第一個模式不是第二個,由於模式按順序匹配,第一個會首先測試是否匹配。請像這樣自由插入一些特殊的狀況來探測匹配的次序。
/articles/2003 不匹配任何一個模式,由於每一個模式要求URL 以一個反斜線結尾。
/articles/2003/03/03/ 將匹配最後一個模式。Django 將調用函數views.article_detail(request, '2003', '03', '03')。
'''

四、設置項是否開啓URL訪問地址後面不爲/ 跳轉至帶有/的路徑

APPEND_SLASH=True

2、有名分組(named group)

  上面的示例使用簡單的、沒有命名的正則表達式組(經過圓括號)來捕獲URL 中的值並以位置 參數傳遞給視圖。在更高級的用法中,可使用命名的正則表達式組來捕獲URL 中的值並以關鍵字 參數傳遞給視圖。服務器

  在Python 正則表達式中,命名正則表達式組的語法是(?Ppattern),其中name 是組的名稱,pattern 是要匹配的模式。 下面是以上URLconf 使用命名組的重寫:app

from django.urls import path,re_path

from app01 import views

urlpatterns = [
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

  這個實現與前面的示例徹底相同,只有一個細微的差異:捕獲的值做爲關鍵字參數而不是位置參數傳遞給視圖函數。例如視圖函數month_archive:函數

def month_archive(request,year,month):
    return HttpResponse(year+"-"+month)

  url訪問調用效果以下:工具

/articles/2005/03/ 
請求將調用views.month_archive(request, year='2005', month='03')函數,而不是views.month_archive(request, '2005', '03')。

/articles/2003/03/03/ 
請求將調用函數views.article_detail(request, year='2003', month='03', day='03')

  在實際應用中,這意味你的URLconf 會更加明晰且不容易產生參數順序問題的錯誤 —— 你能夠在你的視圖函數定義中從新安排參數的順序。固然,這些好處是以簡潔爲代價;有些開發人員認爲命名組語法醜陋而繁瑣。

3、應用include函數實現分發

  爲了實現路由解耦,建立/first_pro/app01/urls.py文件。將/first_pro/first_pro/urls.py中關於app01的路由配置註釋。

一、應用include函數設置/first_pro/app01/urls分發路徑

from django.contrib import admin
from django.urls import path,re_path, include

from app01 import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('timer/', views.timer),

    path('login/', views.login),

    #分發:
    # re_path(r"app01/", include("app01.urls"))
    # 改寫以下:不用app01
    re_path(r"^", include("app01.urls"))
]

(1)分發總結

  1)分發配置爲re_path(r"app01/", include("app01.urls"))時:訪問頁面須要按照以下規則:

    

  2)分發配置爲re_path(r"^", include("app01.urls"))時:訪問頁面不須要添加app01:

    

(2)include使用注意

  1)include() 的正則表達式並不包含一個 $ (字符串結尾匹配符),可是包含了一個斜杆/;

  2)每當Django遇到 include() 時,它將截斷匹配的URL,並把剩餘的字符串發往包含的URLconf做進一步處理。

二、在/first_pro/app01/urls.py進一步配置app01路由

# -*- coding:utf-8 -*-
__author__ = 'Qiushi Huang'


from django.contrib import admin
from django.urls import path,re_path

from app01 import views


urlpatterns = [
    # 路由配置:  路徑--------->視圖函數
    # 正則匹配:r'^articles/2003/$'  以articles/2003/開頭且以articles/2003/結尾
    re_path(r'^articles/2003/$', views.special_case_2003),  # special_case_2003(request)

    # 正則匹配四位的任意數字,多對一
    re_path(r'^articles/([0-9]{4})/$', views.year_archive),  # year_archive(request,1999)

    # 正則匹配一個四位的數字做爲年,還要匹配一個2位的數字做爲月
    # re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),  # month_archive(request,2009,12)

    # 改寫爲有名分組
    re_path(r'^articles/(?P<y>[0-9]{4})/(?P<m>[0-9]{2})/$', views.month_archive)  # month_archive(request,y=2009,m=12)

    # ([0-9]+):匹配前面的數字一次或無限次做爲詳情
    # re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail)
]

4、路由控制——登陸驗證示例

一、在/first_pro/first_pro/urls.py中,編寫路由配置login:

from django.contrib import admin
from django.urls import path,re_path, include

from app01 import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('timer/', views.timer),
    path('login/', views.login)
]

二、修改/first_pro/app01/views.py文件,配置login視圖函數

from django.shortcuts import render,HttpResponse

def login(request):
    print(request.method)  # GET或POST
    # get請求來的話拿到頁面,post請求來的話作校驗
    if request.method == 'GET':
        return render(request, "login.html")
    # post請求
    else:  
        print(request.POST)  # <QueryDict: {'user': ['yuan'], 'pwd': ['123']}>

        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        if user=="yuan" and pwd=="123":
            return HttpResponse("登陸成功")  # 返回字符串
        else:
            return HttpResponse("用戶名或密碼錯誤!")

  取請求的方式:request.method。

  獲得GET請求的時候,經過render方法拿到頁面;獲得POST請求的時候,因爲request.POST獲得一個字典,經過get()方法拿到鍵值。

三、在/first_pro/templates/中,建立login.html模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!--在不指名action的狀況下,默認使用當前頁面的ip地址和端口(同源)-->
    <form action="http://127.0.0.1:8000/login/" method="post">
        用戶名 <input type="text" name="user">
        密碼 <input type="password" name="pwd">
        <input type="submit">
    </form>
</body>
</html>

  action表明的是一個路徑。因爲同源的緣由,能夠寫爲action="http://127.0.0.1:8000/login/",也能夠寫爲action="/login/"。

  因爲提交的是login函數,在urls.py控制器中查找到對應的path('login/', views.login)。找到對應的視圖函數來進行處理。

四、在瀏覽器登陸,輸入正確用戶名和密碼後,執行效果:

  

5、路由控制——反向解析

  在使用Django 項目時,一個常見的需求是得到URL 的最終形式,以用於嵌入到生成的內容中(視圖中和顯示給用戶的URL等)或者用於處理服務器端的導航(重定向等)。人們強烈但願不要硬編碼這些URL(費力、不可擴展且容易產生錯誤)或者設計一種與URLconf 絕不相關的專門的URL 生成機制,由於這樣容易致使必定程度上產生過時的URL。

  獲取一個URL 最開始想到的信息是處理它視圖的標識(例如名字),查找正確的URL 的其它必要的信息有視圖參數的類型(位置參數、關鍵字參數)和值。

Django 提供一個辦法是讓URL 映射是URL 設計惟一的地方。你填充你的URLconf,而後能夠雙向使用它:

  • 根據用戶/瀏覽器發起的URL 請求,它調用正確的Django 視圖,並從URL 中提取它的參數須要的值。
  • 根據Django 視圖的標識和將要傳遞給它的參數的值,獲取與之關聯的URL

  第一種方式是咱們在前面一直討論的用法。第二種方式叫作反向解析URL、反向URL 匹配、反向URL 查詢或者簡單的URL 反查

在須要URL 的地方,對於不一樣層級,Django 提供不一樣的工具用於URL 反查:

  • 在模板中:使用url 模板標籤。
  • 在Python 代碼中:使用django.core.urlresolvers.reverse() 函數。
  • 在更高層的與處理Django模型實例相關的代碼中:使用get_absolute_url() 方法。

一、在模板中實現反向解析

(1)修改配置/first_pro/first_pro/urls.py,設置別名

from django.contrib import admin
from django.urls import path,re_path, include

from app01 import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('timer/', views.timer),

    # path('login/', views.login),
    path('login.html', views.login, name='Log'),
]

(2)修改模板first_pro/templates/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!--在不指名action的狀況下,默認使用當前頁面的ip地址和端口(同源)-->
    <!--<form action="http://127.0.0.1:8000/login/" method="post">-->
    <form action="{% url 'Log' %}" method="post">
        用戶名 <input type="text" name="user">
        密碼 <input type="password" name="pwd">
        <input type="submit">
    </form>
</body>
</html>

  注意form表單的修改。

(3)重新路由地址訪問

  

注意:

  1)模板中的<form action="{% url 'Log' %}" method="post">,在頁面渲染

時改成了<form action="/login/html" method="post">。django在執行return render(request,"login.html")時,並非直接返回的login.html頁面,而是通過了處理(主要是渲染模板語法)。

  2)模板語法有兩種:一種是"{{ }}";一種是"{% %}"。在遇到這兩種狀況時,會進行相應的語法渲染,將渲染的結果交給頁面瀏覽器。

二、使用django.core.urlresolvers.reverse() 函數實現反向解析

(1)在/first_pro/app01/urls.py中給視圖函數路由配置別名:

from django.contrib import admin
from django.urls import path,re_path

from app01 import views

urlpatterns = [
    # 路由配置:  路徑--------->視圖函數
    # 正則匹配:r'^articles/2003/$'  以articles/2003/開頭且以articles/2003/結尾
    re_path(r'^articles/2003/$', views.special_case_2003, name="s_c_2003"),  # special_case_2003(request)

    # 正則匹配四位的任意數字,多對一
    re_path(r'^articles/([0-9]{4})/$', views.year_archive, name="y_a"),  # year_archive(request,1999)

    # 正則匹配一個四位的數字做爲年,還要匹配一個2位的數字做爲月
    # re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),  # month_archive(request,2009,12)

    # 改寫爲有名分組
    re_path(r'^articles/(?P<y>[0-9]{4})/(?P<m>[0-9]{2})/$', views.month_archive)  # month_archive(request,y=2009,m=12)

    # ([0-9]+):匹配前面的數字一次或無限次做爲詳情
    # re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail)
]

(2)修改/first_pro/first_pro/urls.py配置(爲了修改路徑區分更大)

from django.contrib import admin
from django.urls import path,re_path, include

from app01 import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('timer/', views.timer),

    # path('login/', views.login),
    path('login.html/', views.login, name='Log'),

    #分發:
    # re_path(r"app01/", include("app01.urls"))
    # 改寫以下:不用app01
    # re_path(r"^", include("app01.urls"))

    re_path(r"^app01/", include("app01.urls"))
]

(3)在/first_pro/app01/views.py中導入反向解析

from django.shortcuts import render,HttpResponse

# Create your views here.

# 反向解析函數
from django.urls import reverse

def timer(request):
    import time
    ctime = time.strftime('%Y-%m-%d %H:%M:%S')   # 2018-06-30 05:48:11

    url = reverse("s_c_2003")
    url = reverse("y_a", args=(4009,))  # '^articles/([0-9]{4})/$'
    print(url)  # /app01/articles/4009/

    """
    render方法:
        一、幫忙找到timer.html取出裏面的數據;
        二、按照固定的語法({})把變量嵌套到html文件中
    """
    return render(request, 'timer.html', {"date":ctime})    # 使用render方法,返回一個頁面

(4)在瀏覽器訪問http://127.0.0.1:8000/timer/,在程序控制臺輸出:

  /app01/articles/4009/,能夠發現反向解析放在任意視圖中都可以生效。

6、路由控制——名稱空間

  命名空間(英語:Namespace)是表示標識符的可見範圍。一個標識符可在多個命名空間中定義,它在不一樣命名空間中的含義是互不相干的。這樣,在一個新的命名空間中可定義任何標識符,它們不會與任何已有的標識符發生衝突,由於已有的定義都處於其它命名空間中。

  因爲name沒有做用域,Django在反解URL時,會在項目全局順序搜索,當查找到第一個name指定URL時,當即返回。

  咱們在開發項目時,會常用name屬性反解出URL,當不當心在不一樣的app的urls中定義相同的name時,可能會致使URL反解錯誤,爲了不這種事情發生,引入了命名空間。

一、不一樣app定義相同name,URL反解現象

  1)建立app02應用

$ python3 manage.py startapp app02

  2)文件配置修改

/first_pro/first_pro/urls.py:

from django.contrib import admin
from django.urls import path,re_path, include

from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('timer/', views.timer),

    #分發:
    re_path(r"^app01/", include("app01.urls")),
    re_path(r"^app02/", include("app02.urls")),
]

/first_pro/app01/urls.py:

urlpatterns = [
    # 路由配置:  路徑--------->視圖函數
    # re_path("index/", views.index)
    re_path("index/", views.index, name="index")
]

/first_pro/app02/urls.py:

urlpatterns = [
    # 路由配置:  路徑--------->視圖函數
    # re_path("index/", views.index)
    re_path("index/", views.index, name= "index")   # 別名重複
]

/first_pro/app01/views.py:

# 反向解析函數
from django.urls import reverse

def index(request):
    # 反向解析
    return HttpResponse(reverse("index"))

/first_pro/app02/views.py:

# 反向解析函數
from django.urls import reverse

def index(request):
    # 反向解析
    return HttpResponse(reverse("index"))

  3)在頁面上訪問這兩個應用地址

      

  能夠看到兩個都解析爲/app02/index/,能夠看出在不一樣app中出現兩個別名重複,致使了URL反解錯誤。

二、命名空間解決URL反解錯誤

  1)配置命名空間 

/first_pro/first_pro/urls.py:

from django.contrib import admin
from django.urls import path,re_path, include

from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('timer/', views.timer),

    #分發:
    re_path(r"^app01/", include(("app01.urls","app01"))),   # 換爲一個元組
    re_path(r"^app02/", include(("app02.urls","app02"))),
]

  注意:這裏include裏面包含的是一個元組 

/first_pro/app01/views.py:

from django.urls import reverse

def index(request):
    # 反向解析
    return HttpResponse(reverse("app01:index"))

/first_pro/app02/views.py: 

from django.urls import reverse

def index(request):
    # 反向解析
    return HttpResponse(reverse("app02:index"))

  2)在頁面上訪問測試

      

  能夠看到引入了命名空間後,URL反解正常。

7、Django2.0版的path

思考狀況以下:

urlpatterns = [  
    re_path('articles/(?P<year>[0-9]{4})/', year_archive),  
    re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view),  
    re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view),  
    re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view),  
]

存在兩個問題:

  第一個問題,函數 year_archive 中year參數是字符串類型的,所以須要先轉化爲整數類型的變量值,固然year=int(year) 不會有諸如如TypeError或者ValueError的異常。那麼有沒有一種方法,在url中,使得這一轉化步驟能夠由Django自動完成?

  第二個問題,三個路由中article_id都是一樣的正則表達式,可是你須要寫三遍,當以後article_id規則改變後,須要同時修改三處代碼,那麼有沒有一種方法,只需修改一處便可?

  在Django2.0中,可使用 path 解決以上的兩個問題。

一、基本示例

from django.urls import path  
from . import views  
urlpatterns = [  
    path('articles/2003/', views.special_case_2003),  
    path('articles/<int:year>/', views.year_archive),  
    path('articles/<int:year>/<int:month>/', views.month_archive),  
    path('articles/<int:year>/<int:month>/<slug>/', views.article_detail),  
]  

基本規則:

  • 使用尖括號(<>)從url中捕獲值。至關於有名分組,括號內的int是path內置的轉換器
  • 捕獲值中能夠包含一個轉化器類型(converter type),好比使用 <int:name> 捕獲一個整數變量。若果沒有轉化器,將匹配任何字符串,固然也包括了 / 字符。
  • 無需添加前導斜槓

對first_pro進行修改:

/first_pro/first_pro/urls.py:

from django.contrib import admin
from django.urls import path,re_path, include

from app01 import views

urlpatterns = [
    path("articles/<int:year>/",views.path_year)   # path_year(request,2010)
]

/first_pro/app01/views.py:

from django.shortcuts import render,HttpResponse

# Create your views here.
def month_archive(request,y,m):
    print(m)   # 11
    print(type(m))   # <class 'str'>
    print(y)   # 2010
    print(type(y))   # <class 'str'>
    return HttpResponse(y+"-"+m)


def path_year(request, year):
    print(year)             # 2010
    print(type(year))    # <class 'int'>
    return HttpResponse("path year")

運行效果:

  

  同時控制檯輸出:2010和<class 'int'>,說明使用<int:year>捕獲的是一個整數變量。

如下是根據 2.0官方文檔 而整理的示例分析表:  

二、path轉化器(path converters)

  文檔原文是Path converters,暫且翻譯爲轉化器。

Django默認支持如下5個轉化器:

  • str,匹配除了路徑分隔符(/)以外的非空字符串,這是默認的形式
  • int,匹配正整數,包含0
  • slug,匹配字母、數字以及橫槓、下劃線組成的字符串。(變量經常使用)
  • uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00
  • path,匹配任何非空字符串,包含了路徑分隔符 (捕獲任何字符串,非空便可,可是?不行做用是分隔符)

三、註冊自定義轉化器

對於一些複雜或者複用的須要,能夠定義本身的轉化器。轉化器是一個類或接口,它的要求有三點:

  • regex類屬性,字符串類型
  • to_python(self, value)方法,value是由類屬性regex所匹配到的字符串,返回具體的Python變量值,以供Django傳遞到對應的視圖函數中
  • to_url(self, value)方法,和to_python相反,value是一個具體的Python變量值,返回其字符串,一般用於url反向引用

應用示例:

(1)建立/first_pro/app01/url_convert.py文件

class MonConvert:
    # regex 類屬性,字符串類型。所以不能隨便取其餘名字
    regex = "[0-9]{2}"   # 兩位的數字

    # to_python(self, value)方法,value是由類屬性regex所匹配到的字符串,返回具體的Python變量值
    def to_python(self, value):
        return int(value)

    # to_url(self, value) 方法,value是一個具體的Python變量值,返回其字符串,一般用於url反向引用
    def to_url(self, value):
        return '%04d' % value

(2)註冊定義的url轉換器:/first_pro/first_pro/urls.py

from django.contrib import admin
from django.urls import path,re_path, include, register_converter   # 注意register_converter模塊引入

from app01.url_convert import MonConvert   # 引入建立的轉換器

# 註冊定義的url轉換器
register_converter(MonConvert, "mm")

from app01 import views

urlpatterns = [
    path("articles/<mm:month>", views.path_month)
]

(3)自定義轉化器/first_pro/app01/urls.py

from django.shortcuts import render,HttpResponse

# 自定義轉化器
def path_month(request, month):
    print(month, type(month))
    return HttpResponse("path month....")

(4)訪問驗證

  

  程序控制臺輸出:12 <class 'int'>,能夠看到month被轉化爲了數字類型。

相關文章
相關標籤/搜索