仿製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是不同的。