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

原文地址:https://www.cnblogs.com/yinsedeyinse/p/13311344.html