Django学习笔记二十三——Django之admin模块的中URL的分发思路
在前面分析admin组件的过程中我们了解了admin组件的实现流程,今天我们来完成一个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是不同的。