1. 程式人生 > >談談Python之Django搭建企業級官網(第三篇下部)

談談Python之Django搭建企業級官網(第三篇下部)

Django項目 原因 解釋 新用戶 rev type shortcuts 編寫 out

轉載請註明來源地址和原作者(CFishHome)

前沿

上一篇文章我們學習了URL與視圖函數的映射、傳遞參數的三種方式、轉換器的簡單使用和include函數分層映射管理。接下來這一篇文章著重介紹Path、re_path、include、reverse、redirect函數的使用和自定義URL轉換器。學完這些內容,相信我們對URL和視圖都會有了一定的了解和認識。為了讓每篇文章具有一定的獨立性,我決定每篇文章都重新新建一個項目,便於測試和調試。

預備

首先,我們在Pycharm重新新建一個名為book_project的Django項目,然後打開CMD窗口進入虛擬環境,接著跳轉到該項目的目錄下(含有manage.py),再依次執行創建app命令,如下圖:

技術分享圖片
創建完成後,該項目的樣子大概是醬紫的:
技術分享圖片

模擬前端和後端

我們想實現:front前端擁有前端首頁和前端登陸頁面,而cms後端擁有後端首頁和後端登陸頁面,然後分別輸入配置好的URL對這四個頁面進行訪問。
首先,為cms和front兩個app手動添加urls.py文件。然後分別按以下步驟添加或修改項目文件代碼:
(1)為cms的urls.py文件添加以下代碼:

from django.urls import path
from . import views

urlpatterns = [
    path("", views.index),
    path("login/", views.login)
]

(2)為front的urls.py文件添加以下代碼:

from django.urls import path
from . import views

urlpatterns = [
    path("", views.index),
    path("login/", views.login)
]

(3)為cms的views.py文件添加以下代碼:

from django.http import HttpResponse

def index(request):
    return HttpResponse("後端首頁")

def login(request):
    return HttpResponse("後端登陸頁面")

(4)為front的views.py文件添加以下代碼:

from django.http import HttpResponse

def index(request):
    return HttpResponse("前端首頁")

def login(request):
    return HttpResponse("前端登陸頁面")

(5)為book_project這個主包的urls.py文件添加以下代碼:

from django.urls import path,include

urlpatterns = [
    path(‘‘, include("front.urls")),
    path(‘cms/‘, include("cms.urls"))
]

如果學習了上一篇文章的內容,我相信這些代碼的編寫是完全OK的。好,我們運行一下項目(註意,我們是新建的項目,記得在勾選單一實例運行。)。
運行結果如下(完美,成功運行~):
技術分享圖片

實際需求

模擬自動跳轉新用戶登陸頁面

實際需求:要想實現一個類似於知乎主頁一樣,若是新用戶第一次登陸知乎,不管哪個頁面都會跳轉到登陸賬號的頁面,對於這一需求,我們可以通過以下模擬實現(為了更好觀察代碼,一樣的代碼部分我直接提示省略):
(1)修改front前臺的index視圖函數

#省略上面代碼
from django.shortcuts import redirect  # 導入重定向redirect函數,最好記住redirect函數源於哪個模塊,因為要經常重定向。

def index(request):
    # ?username=xxx
    username = request.GET.get(‘username‘)  # 當用戶輸入127.0.0.1:8000,檢測到沒有賬戶則跳轉到註冊頁面,當輸入http://127.0.0.1:8000/?username=1正常顯示前臺首頁。
    if username:
        return HttpResponse(‘前臺首頁‘)
    else:
        return redirect(‘/login/‘)  # 跳轉通過redirect函數來進行頁面重定向,重新訪問指定的url頁面
#省略下面代碼

修改完代碼後,按下Ctrl+S保存(自動重新編譯項目)。註意觀察,輸入127.0.0.1:8000會發現自動跳轉到127.0.0.1:8000/login/。

重定向代碼的缺陷

其實還存在這樣一種情況,假如老板或項目經理突然腦袋秀逗了,要求你更改網頁的URL,將127.0.0.1:8000/login/更改為127.0.0.1:8000/signin/,假如這時還采用上面的代碼進行網頁重定向,那你會非常抓狂,舉我們這個項目栗子來說,不單只要修改為下面這樣:

urlpatterns = [
    path("", views.index),
    path("signin/", views.login)
]

還要修改重定向位置的代碼這樣:

def index(request):
    # ?username=xxx
    username = request.GET.get(‘username‘)  # 當用戶輸入127.0.0.1:8000,檢測到沒有賬戶則跳轉到註冊頁面,否則正常顯示前臺首頁。
    if username:
        return HttpResponse(‘前臺首頁‘)
    else:
        return redirect(‘/singin/‘)  # 跳轉通過redirect函數來進行頁面重定向,重新訪問指定的url頁面

如果公司網站項目十分龐大的話,那可能還有很多地方都要修改,這十分耗時也費力。所以,我們推薦為URL命名來解決這一問題,給URL取個名字,只要調用reverse反轉URL的名字而不是直接重定向寫死的URL,那麽無論URL怎麽修改也影響不到其他地方。

Path函數

在學習URL命名之前,先詳細學習下Path函數的使用。
path 函數的定義為:

path(route,view,name=None,kwargs=None)  。

以下對這幾個參數進行講解。

  1. route 參數: url 的匹配規則。這個參數中可以指定 url 中需要傳遞的參數,上一篇文章已講解,這個參數不再贅述。請移步到談談Python之Django搭建企業級官網(第三篇上部)
  2. view 參數:可以為一個視圖函數或者是 類視圖.as_view() 或者是 django.urls.include() 函數的返回值。
  3. name 參數:這個參數是給這個 url 取個名字的,這在項目比較大, url 比較多的時候用處很大。這個參數就是用於URL命名。
  4. kwargs 參數:有時候想給視圖函數傳遞一些額外的參數,就可以通過 kwargs 參數進行傳遞。這個參數接收一個字典。會將foo=bar作為關鍵字實參傳入視圖函數,所以視圖函數要有形參來接收實參。
    比如以下的 url 規則:
    from django.urls import path
    from . import views
    urlpatterns = [
        path(‘blog/<int:year>/‘, views.year_archive, kwargs={‘foo‘:‘bar‘}),  
    ]

    那麽以後在訪問 blog/1991/ 這個url的時候,會將{‘foo‘:‘bar‘}作為關鍵字參數傳給 year_archive函數。year_archive視圖函數的形參中最後添加一個kwargs參數來接收這個額外的參數。

URL命名

學習完path函數各參數,相信都知道該函數的name參數就是用於URL命名的了。
接下來修改front包的urls.py文件代碼如下:

urlpatterns = [
    path("", views.index),
    path("login/", views.login, name=‘login‘)
]

再次修改front包的views.py文件代碼如下:

#省略上面代碼
from django.shortcuts import redirect, reverse  # 註意這裏添加了reverse函數,reverse函數用於將指定URL名字反轉成URL。

def index(request):
    # ?username=xxx
    username = request.GET.get(‘username‘)  # 當用戶輸入127.0.0.1:8000,檢測到沒有賬戶則跳轉到註冊頁面,否則正常顯示前臺首頁。
    if username:
        return HttpResponse(‘前臺首頁‘)
    else:
        # reverse(‘login‘)函數返回‘/login/’
        return redirect(reverse(‘login‘))  # 跳轉通過redirect函數來進行頁面重定向,重新訪問指定的url頁面(相當於重新訪問127.0.0.1:8000/login/)
#省略下面代碼

按下Ctrl+S保存,輸入127.0.0.1:8000成功跳轉到127.0.0.1:8000/login登陸頁面。

應用命名空間

在公司實際開發中,如果公司裏的多個人同時負責網站的開發,而且A同事負責開發front的app,B同事負責開發cms的app,那麽由於兩個app都有首頁和登陸頁面,那麽有可能url的name屬性可能會相同沖突。在多個app之間,有可能產生同名的url,這時候為了避免反轉url的時候產生混淆,可以使用應用命名空間來做區分。定義應用命名空間非常簡單,只要在“app”的“urls.py”中定義一個叫做“app_name”的變量,來指定這個應用的命名空間即可。
就好比我們的項目,將front包裏的urls.py和views.py文件和cms包裏的urs.py和views.py文件分別為URL映射命名為index和login,如下圖所示:
技術分享圖片
運行項目,輸入127.0.0.1:8000自動跳轉到127.0.0.1:8000/cms/login網頁,而不是之前的127.0.0.1:8000/login網頁,由於多個app的URL擁有相同的名字,所以Django在執行reverse函數反轉URL時懵逼了。為了解決這個問題,我們將采用應用命名空間。
修改front包的urls.py文件的代碼如下(django在執行到app時,會自動讀取這個應用命名空間並將這個名字作為app的名字):

from django.urls import path
from . import views

app_name = "front"  # 添加了應用命名空間

urlpatterns = [
    path("", views.index, name="index"),
    path("login/", views.login, name=‘login‘)
]

以後在做反轉的時候就可以使用“應用命名空間:url名稱”的方式進行反轉。示例代碼如下修改front包的views.py文件的代碼如下:

def index(request):
    # ?username=xxx
    username = request.GET.get(‘username‘)  # 當用戶輸入127.0.0.1:8000,檢測到沒有賬戶則跳轉到註冊頁面,否則正常顯示前臺首頁。
    if username:
        return HttpResponse(‘前臺首頁‘)
    else:
        return redirect(reverse(‘front:login‘))  # 這裏為URL名字前面添加front應用命名空間名

按下Ctrl+S保存,輸入127.0.0.1:8000自動跳轉到127.0.0.1:8000/login網頁,成功了,Django不會再懵逼了。

include函數

在上一篇文章我們知道,在項目不斷龐大以後,經常不會把所有的 url 匹配規則都放在項目的 urls.py 文件中,而是每個 app 都有自己的 urls.py 文件,在這個文件中存儲的都是當前這個 app 的所有url 匹配規則。然後再統一include到項目的 urls.py 文件中。 include 函數有多種用法,這裏講下幾種常用的用法:
(1)include(pattern,namespace=None) :直接把其他 app 的 urls 包含進來。
之前的include用法,舉個栗子(下面的代碼僅用於解釋用法,不是將代碼添加到項目):

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

urlpatterns = [
        path(‘admin/‘, admin.site.urls),
        path(‘book/‘,include("book.urls"))
]

我們可以發現這一種用法其實該函數還有參數2指定實例命名空間,默認是None。但是在使用實例命名空間之前,必須先指定一個應用命名空間,不先指定應用命名空間會報錯。一個app可以創建多個實例,也就是可以使用多個url映射同一個app,所以這就產生一個問題。以後在做反轉的時候,如果使用應用命名空間,那麽就會發生混淆。
將book_project主包的urls.py文件代碼修改如下:

from django.urls import path,include

urlpatterns = [
    path(‘‘, include("front.urls")),
    path(‘cms1/‘, include("cms.urls")),  # 這兩行代碼代表輸入127.0.0.1:8000/cms1或127.0.0.1:8000/cms2能映射到cms.urls內部的url路徑。
    path(‘cms2/‘, include("cms.urls"))
]

為cms包的urls.py文件添加應用命名空間:

app_name = "cms"

為cms包的views.py文件修改代碼如下:

def index(request):
    # ?username=xxx
    username = request.GET.get(‘username‘)  # 當用戶輸入127.0.0.1:8000,檢測到沒有賬戶則跳轉到註冊頁面,否則正常顯示前臺首頁。
    if username:
        return HttpResponse(‘後臺首頁‘)
    else:
        return redirect(reverse(‘cms:login‘))  # 這裏使用了應用命名空間反轉URL

經過上面代碼的修改,這時候按下Ctrl+S保存,輸入127.0.0.1:8000/cms1成功跳轉到127.0.0.1:8000/cms1/login,但是當輸入127.0.0.1:8000/cms2卻自動跳轉到了127.0.0.1:8000/cms1/login,what?我的127.0.0.1:8000/cms2/login哪裏去了?導致上面的原因就是前面介紹的一個app可以創建多個實例,也就是可以使用多個url映射同一個app,所以這就產生一個問題。以後在做反轉的時候,如果使用應用命名空間,那麽就會發生混淆。為了避免這個問題,我們可以使用實例命名空間。實例命名空間也是非常簡單,只要在“include”函數中傳遞一個“namespace”變量即可。
在上面代碼的基礎上繼續修改代碼,修改cms包的views.py文件代碼如下:

def index(request):
    username = request.GET.get(‘username‘)
    if username:
        return HttpResponse("後端首頁")
    else:
        current_namespace = request.resolver_match.namespace  # 返回當前app對應的實例命名空間(cms1或cms2)
        return redirect(reverse(‘%s:login‘ % current_namespace))  # 相當於"cms1:login"或”cms2:login“

接著修改book_project主包的urls.py文件代碼如下:

from django.urls import path,include

urlpatterns = [
    path(‘‘, include("front.urls")),
    path(‘cms1/‘, include("cms.urls", namespace="cms1")),  # 使用了實例命名空間namespace
    path(‘cms2/‘, include("cms.urls", namespace="cms2"))  
]

這時候按下Ctrl+S保存,輸入127.0.0.1:8000/cms1成功跳轉到127.0.0.1:8000/cms1/login,然後輸入127.0.0.1:8000/cms2也成功跳轉到了127.0.0.1:8000/cms2/login頁面。如下圖:
技術分享圖片
(2)include((pattern_list,app_namespace),namespace=None) :“include”函數的第一個參數既可以作為一個字符串,也可以作為一個元組,如果是元組,那麽元組的第一個參數是子“urls.py”模塊的字符串,元組的第二個參數是應用命名空間,也就是說應用命名空間既可以在子“urls.py”種通過"app_name"指定,也可以在“include”函數中指定。
將book_project主包的urls.py文件代碼修改如下:

from django.urls import path, include
from front import views

urlpatterns = [
    path(‘‘, include(([
                          path(‘‘, views.index, name="index"),
                          path("login/", views.login, name=‘login‘)
                      ], "front"))),  # 註意
    path(‘cms1/‘, include("cms.urls", namespace="cms1")),
    path(‘cms2/‘, include("cms.urls", namespace="cms2"))
]

上面的代碼相當於完全忽略了front包的urls.py文件的作用,因為已經被include函數的第一個參數列表給替代了,所以urls.py文件的“app_name = "front"”指定的應用命名空間自然也失效了。這時運行代碼完全OK,跟之前的一摸一樣,輸入127.0.0.1:8000自動跳轉到127.0.0.1:8000/login頁面。
(3)include(pattern_list) :可以包含一個列表或者一個元組,這個元組或者列表中又包含的是 path 或者是 re_path 函數(該函數後面會講)。
這個函數跟上一個函數差不多,也是用含有path函數的列表或元組替代了之前的pattern。但是需要註意的是,因為這樣會忽略了front包的urls.py文件的作用,所以urls.py文件的“app_name = "front"”指定的應用命名空間自然也失效了。那麽如果你映射的視圖函數內部進行反轉URL時指定了應用命名空間,那麽將會報錯,會提示找不到front命名空間,如下:

技術分享圖片

所以,綜上建議,如果你映射的視圖函數內部進行反轉URL時指定了應用命名空間,最好調用include((pattern_list,app_namespace),namespace=None) 函數,在指定列表或元組 的同時也指定應用命名空間。

後面的內容明天補充.....

談談Python之Django搭建企業級官網(第三篇下部)