Django 路由分发原理探究

路由控制之简单配置

  • 看urls.py中的导包from django.urls import path,re_path
    path和re_path区别在于re_path是用正则匹配路由路径,而path就是纯字符串匹配
    所以这里用re_path是为了展示1.0也是有这个功能
    在urls中添加路由列表,第一个参数是url路径中一旦匹配到这个字符串,就会执行views中的函数,
    第一个参数是支持正则写法的,比如以case/case01开头,但不限定结尾

    可以看到只要是以case/case01开头 后面无论添加什么 都返回一样的内容


    为了演示效果,views文件中,导入HttpResponse方法先.

  • 路由正则分组概念
    现在先在views新写一个函数case02,可以看到新增了一个新参,后面会提到

    在urls中我们对路由列表的正则表达式 添加了一个括号(),这个在正则代表分组的意思,这个分组如果配到的话,会连同request一起传给views里的函数,所以必须要有2个形参

    访问结果如下,年份会随着你的url变化而变化,

    然而路由中的分组可以有很多个,注意的一点就是无论是几个分组,都要相应加上request一起传给函数,函数的形参接受数量要一直

  • 路由正则分组规则不严格,导致从上至下解析错误解析的问题
    我们看下面,case02这里我们把前面正则的$结尾限定符号去掉.那么当访问的Url为http://127.0.0.1:8000/case/2088/02/时候会自上而下优先访问case02函数

    本意是要访问case03的,结果因为2条正则规则都符合,自上而下运行,就优先执行了上面一条规则,运行了case02, 要避免这个情况就把$加上去re_path(r"^case/([0-9]{4,})/$",case02),

    分组总结:

  • 如果从URL中获取一个值,那么在周围放置一堆圆括号

  • 根路径后面的第一个路径不需要加反斜杠,比如case是对的/case反而是错误的

  • 每个正则前面建议加上'r' 代表字符串中任何字符都无须转义
    其他的例子,便于理解

路由控制之有名分组

  • 上面的例子中函数传参是根据位置传的,但是如果我们需求是根据形参名传呢????
    我们需要在urls路由控制参数中用固定的?P<变量名>添加到正则的匹配语法中去,比如([0-9]{4,})就变成了?P<year>([0-9]{4,})
    所以re_path(r"^case/(?P<year>[0-9]{4,})/(?P<mon>[0-9]{2})/$",case03)会传参数request,year= mon=
    我们在case03中故意把年份和月份写反,

    但是输出结果还是正常

路由控制之分发解耦

前面的例子中,我们把项目所有的路由分发策略都写在了项目下的urls.py,如果我们有100个app呢,如果每个app下又有100个子页面呢? 我们需要进一步解耦,在项目路由表中按照应用名分发,然后在应用的包中再创建一个urls.py,用于该应用下的路由分发,这样就解耦开来了

  • 第一步需要在项目urls中导入include包 from django.urls import path,re_path,include
  • 第二步在应用下创建urls文件,然后在项目路由中添加分发规则,如果是my_app开头的全部分发到my_app.urls中
    re_path(r"my_app/",include("my_app.urls"))
  • 第三部my_pp中的urls创建规则
    from django.urls import path,re_path
    
    from my_app.views import *
    urlpatterns = [
        re_path(r"^case/(?P<year>[0-9]{4,})/(?P<mon>[0-9]{2})/$",case03),
        path("mytest/",my_test)
    ]
    
  • 访问页面
  • 整个代码页面

路由控制之登录验证示例

如前端网页视图,我们平时用get访问是访问网页,但是当我们在输入了账号密码按回车后又是需要递交参数的,

如果我们get访问的url和<form action="http://127.0.0.1:8000/test/" method="post"> 都是http://127.0.0.1:8000/test/ 要怎么区分这两次访问呢?
==注:如果form action 递交数据还是跳回get的url地址,那么前面的ip可以省略 <form action="/test/" method="post">
在全局的路由分发中urls代码如下

from my_app.views import *
urlpatterns = [
    path('test/', beijing_time),
]

视图中的views代码如下: 可以看到这个形参request会把请求的方式和内容,并且把提交的信息以字典形式可以轻松获取.

def beijing_time(request):
    import time
    c = time.time()
    b= time.localtime(c)
    a = time.strftime("%Y-%m-%d %H:%M:%S")
    if request.method=="POST": #这里区分提交方式,如果是POST就开始验证账号密码.
        if request.POST.get("user") == "sxf" and request.POST.get("pwd")=="123":
            return HttpResponse("登陆成功")
        else:
            return HttpResponse("登陆失败")
    elif request.method=="GET":
        return render(request,"test.html",{"data":a})

路由控制之反向解析1

还是上面的例子,根据路由分发规则,我们get访问的url是http://127.0.0.1:8000/test/,然后POST请求的地址也是http://127.0.0.1:8000/test/
POST请求的HTML文件和GET请求是同一个HTML文件,但是考虑到后期get的请求地址会变动,那么我们改了get请求的路由规则,比如把test改成login

from my_app.views import *
urlpatterns = [
    path('login/', beijing_time),
]

但是在我们输入了账号密码递交,却显示404了,因为action的网址还没改,因为把他写死了.

我们需要用反向解析给路由分发器中取个别名 name="log"

from my_app.views import *
urlpatterns = [
    path('login/', beijing_time,name="log"),
]

然后HTML文件中用蒙版语法引用这个POST地址,语法为{% url 别名 %}
<form action="{% url 'log' %}" method="post">
这样无论前面路由地址怎么更改,都可以获取到路由中的地址路径

流程控制之反向解析2

反向解析还有种用法,这种用法是在view函数中根据路由分发器中的name变量

# 项目的路由分发器代码如下
urlpatterns = [
    re_path(r"my_app/",include("my_app.urls"))
]

my_app中的分发路由如下

urlpatterns = [
    re_path(r"^case3/([0-9]{4,})/([0-9]{2})/$",case03,name = "mytest_reverse3"),  
    # 路径为my_app/case/2019/02/
    path("mytest/2003",my_test,name="mytest_reverse")  
    #路径为my_app/mytest/2003
]

views中的函数如下

def case03(request,mon,year):
    print(reverse("mytest_reverse2",args=(2004,"09",)))
    return HttpResponse("你输入的年份是%s,月份是%s"%(year,mon))

def my_test(request):

    print(reverse("mytest_reverse"))
    return HttpResponse("my_test页面")
  • 以路径http://127.0.0.1:8000/my_app/mytest/2003为例,会运行my_test函数,会打印输出/my_app/mytest/2003
    reverse("mytest_reverse")会解析出mytest_reverse这个别名的url地址,这里的url是写死的,下面举例一个正则的
  • 以路径my_app/case/2019/02/为例,会运行 case03 函数,别名是mytest_reverse3,那么在case03函数中reverse("mytest_reverse2",args=(2004,"09",))
  • 因为分发路由中的re_parth中有了2个分组,因此我们在reverse函数中要添加2个参数,参数必须符合分组的正则表达规则,打印输出的内容为/my_app/case/2004/09/
  • 这里强调的一点是:函数里的reverse()进行的反向解析和所在哪个函数没有关系 假设我们访问路径为http://127.0.0.1:8000/my_app/mytest/2003

    可以看到我们把2个反向解析都放在一个My_test函数中,他的会因为路径找到my_test这个函数执行,然后reverse反向解析会根据别名解析到下面2个路由分发的规则中
    根据mytest_reverse2别名解析里面的url解析规则,在根据2个分组传入参数2004,09,输出路径/my_app/case/2004/09/
    根据mytest_reverse别名解析里面的url解析规则,解析出固定的路径mytest/2003
urlpatterns = [
    re_path(r"^case3/([0-9]{4,})/([0-9]{2})/$",case03,name = "mytest_reverse2"),  
    # 路径为my_app/case/2019/02/
    path("mytest/2003",my_test,name="mytest_reverse")  
    #路径为my_app/mytest/2003
]

路由控制之名称空间

我们因为需要反向解析,在应用的分发器中会加入Name参数,以便反向解析动态获取一些数值

但是反向解析是根据name这个参数来的,不同的应用下name取值可能会重复,就会导致下面这个情况,my_app和my_app2下有相同的函数index,并且他们的命名都是name
所以my_app下的反向解析会被my_app2的反向解析覆盖,出现了运行各自应用下的index,但是反向解析出现了错误,所以我们需要把命名空间也按照不同的应用进行分隔.

按照应用分隔命名空间需要2步
第一:项目总路由那边include里面要添加元祖,参数一是按应用的分发路由,参数二是按应用定义的命名空间
第二:我们只需要后期reverse函数中使用命名空间:函数名 就可以在反向解析中获取正确的内容了

看正确的结果

原文地址:https://www.cnblogs.com/Young-shi/p/15068324.html