1. 程式人生 > 實用技巧 >仿製Django的admin元件完成的Xadmin元件設計——url分發

仿製Django的admin元件完成的Xadmin元件設計——url分發

在前面分析admin元件的過程中我們瞭解了admin元件的實現流程,今天我們來完成一個url的設計,再看看整個URL的設計思路

URL設計思路

admin元件下的URL設計

admin元件完成了每個app下的model裡的ORM都設計了增刪改查的URL,比方我們在app01下的model裡有個book類,可以看看他的這四個URL是怎麼設計的。

127.0.0.1:8000/admin/app01/book/

127.0.0.1:8000/admin/app01/book/add

127.0.0.1:8000/admin/app01/book/1/change/

127.0.0.1:8000/admin/app01/book/1/delete/

每個模型下都有這四條URL,但是如果有7個數據Model就會有28條URL,顯然Django不是直接寫死這28條URL的,那是怎麼寫出來的呢?

首先我們要了解url指的是具體哪一部分:

整個URL應該是這樣的

http://127.0.0.1:admin/app01/book/1/

前面的http是協議名,後面緊跟的是地址和埠號下面的那一段用/分割的才是url(有些情況下還有引數,是用?來開頭的,引數部分不算URL)也就是說我麼需要設計的URL就是整條用/來分隔的路徑引數,而我們在路由裡匹配的url也是這個路徑。

我們注意看一下第一級的URL裡面帶了下面的一句程式碼

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

進去看一看django的原始碼裡的urls方法

def urls(self) -> Tuple[List[URLResolver], str, str]: ...

可以發現,這個url返回的是一個元組,主要的引數是第一個列表,後面兩個字串引數暫時沒什麼用,先放個None就行了。

也就是這樣的效果

def test01(response):
    return HttpResponse('test01')

def test02(request):
    return HttpResponse('test02')
urlpatterns
= [ url('^abc/',([ url(r'test01/',test01), url(r'^test02/',test02) ],None,None)), ]

上面就是一個一級的路由的分發過程,首先是前面的abc/,後面就按照test01和test02的url分發,整個就是個1*2的URL分發過程。

同理,在一級分發的過程中我們還可以通過巢狀來完成二級的分發:

 1 def test01(response):
 2     return HttpResponse('test01')
 3 
 4 def test02(request):
 5     return HttpResponse('test02')
 6 
 7 def test04(request):
 8     return HttpResponse('test04')
 9 
10 def test05(response):
11     return HttpResponse('test05')
12 
13 
14 
15 urlpatterns = [
16     
17     url('^abc/',([
18         url(r'test01/',test01),
19         url(r'^test02/',test02)
20     ],None,None)),
21 ]    
22 urlpatterns = [
23     
24     url('^abc/',([
25         url(r'test01/',test01),
26         url(r'^test02/',test02),
27         url(r'^test03/',([
28             url(r'test04/',test04),
29             url(r'^test05/',test05),
30         ],None,None))
31     ],None,None)),
32 ]

上面的這段程式碼就是個二級分發的過程,分別對應了下面幾個url(前面的地址和埠號省略了。)

abc/test01
abc/test02
abc/test03/test04
abc/test03/test05

注意test03是沒有檢視對應的,直接分發下去了。

為Xadmin設計新的URL

知道了admin的URL設計思路,我們就可以為我們的Xadmin設計一套URL

首先,我們可以把整個URL的生成部分放到一個函式中,通過函式返回的元祖來完成URL的分發

def get_urls():
    temp = []
    temp.append(url('app01/book/',test01))
    temp.append(url('app01/publish/',test02))
    temp.append(url('app01/author/',test04))
    return temp,None,None

urlpatterns = [
    url(r'^Xadmin',get_urls())
]

這樣就完成了一個1*3的分發。但是有個最大的問題,我們在這裡把所有的類都寫死了(book,publish,author),而我們的需求是能夠操作所有註冊的表,所以這裡就不能寫死。

app名稱和models名稱的獲取

因為我們在用上面的方式新增url的時候,必須要拿到字串型別的app的名稱和models的名稱,前提是我們已經在各個app下的admin.py下做好了註冊,那麼看一看下面的程式碼

from Xadmin.service.Xadmin import site

def get_urls():
    print(site._registry)
    temp = []
    temp.append(url('app01/book/',test01))
    temp.append(url('app01/publish/',test02))
    temp.append(url('app01/author/',test04))
    return temp,None,None

urlpatterns = [
    url(r'^Xadmin',get_urls())
]

這裡要注意的是,由於我們新做的Xadmin元件,所以這裡要匯入的是我們新建立的site,不是django原始碼裡的admin.site。

有個知識點要注意一下:

get_urls()方法是在django啟動的時候就執行了,不是在使用者訪問Xadmin這個url的時候才執行

所以下面的這一段總結只講get_urls這個函式,後面的url的列表就先不關注了。看一看django專案執行以後打印出來的資料

{<class 'app01.models.Books'>: <Xadmin.service.Xadmin.ModelAdmin object at 0x7ffb1792d1d0>, 
<class 'app01.models.Publisher'>: <Xadmin.service.Xadmin.ModelAdmin object at 0x7ffb1792d208>,
<class 'app02.models.Order'>: <Xadmin.service.Xadmin.ModelAdmin object at 0x7ffb1792d470>}

site._registry對應的資料是一個字典,字典的key就是ORM的類,而value就是一個配置類。所以我們可以用for迴圈的方式拿到各個類

def get_urls():
    for model,admin_class_obj in site._registry.items():
        print(model)
        print(admin_class_obj)

##########輸出##########
<class 'app01.models.Books'>
<Xadmin.service.Xadmin.ModelAdmin object at 0x7f0dd58ac1d0>
<class 'app01.models.Publisher'>
<Xadmin.service.Xadmin.ModelAdmin object at 0x7f0dd58ac208>
<class 'app02.models.Order'>
<Xadmin.service.Xadmin.ModelAdmin object at 0x7f0dd58ac470>

但是一定要注意到這個model是一個類,不是一個字串,還好Django給我們預留了下面的方法

def get_urls():
    for model,admin_class_obj in site._registry.items():
        print(model._meta.model_name)          #獲取model名稱
        print(model._meta.app_label)             #獲取app名稱

##########輸出##########
books
app01
publisher
app01
order
app02

用上面的方法獲取到註冊的模型類,然後用字串拼接的方式就可以直接做好URL的一級分發設計工作了。

def get_urls():
    temp = []
    for model,admin_class_obj in site._registry.items():
        app_name = model._meta.app_label
        model_name = model._meta.model_name
        
        temp.append(url(r"^{0}/{1}/".format(app_name,model_name),test01))

    
    return temp,None,None

二級分發

一級分發我們完成了各個table的獲取,但是每個table都有對應的增刪改查的操作,這些操作是放在二級分發來實現的。需要的就是我們在test01處再放一個元組,當然也可以抽到另一個函式中

 1 def list_view(request):
 2     return HttpResponse('list_view')
 3 
 4 def add_view(request):
 5     return HttpResponse('add_view')
 6 
 7 def change_view(request,id):
 8     return HttpResponse('change_view')
 9 
10 def delete_view(request,id):
11     return HttpResponse('delete_view')
12 def get_urls_2():
13     temp = []
14 
15     temp.append(url(r'^$',list_view))
16     temp.append(url(r'^add/$',add_view))
17     temp.append(url(r'^(\d+)/change/$',change_view))
18     temp.append(url(r'^(\d+)/delete/$',delete_view))
19 
20     return temp,None,None
21 
22 
23 def get_urls():
24     temp = []
25     for model,admin_class_obj in site._registry.items():
26         app_name = model._meta.app_label
27         model_name = model._meta.model_name
28         
29         temp.append(url(r"^{0}/{1}/".format(app_name,model_name),get_urls_2()))
30 
31     
32     return temp,None,None
33 
34 urlpatterns = [
35     url(r'^Xadmin/',get_urls())
36 ]
二級分發

注意點:

在上面的程式碼中我們構造了四個檢視函式,比方說查詢的頁面對應的檢視,book和publisher的對應的是一個檢視,但是url是不同的。