Django 1.X的路由系统

一、前言

在django程序中,可以通过urls.py文件对所有的url进行任务的分配,URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表。
你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。
Django 1.11版本 URLConf官方文档

Django大致工作流程
1、客户端发送请求(get/post)经过web服务器、Django中间件、 到达路由分配系统 ;
2、路由分配系统根据提取 request中携带的的url路径(path)与视图函数映射关系列表中,匹配到1个视图函数,index(request)执行;(这篇文章的内容)
3、视图函数 使用原生SQL或者ORM去数据库拿到数据,在服务端进行渲染(模板+数据渲染);
4、视图函数return一个 response对象 返回客户端。

二、URLconf配置

  • 基本格式
from django.conf.urls import url
urlpatterns = [
     url(正则表达式, views视图函数,参数,别名),
]

例,url.py:

from django.conf.urls import include, url
from django.contrib import admin
from app1 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/$', views.index),
  ]
  • 参数说明
    url()函数可以传递4个参数,其中2个是必须的:regex和view,以及2个可选的参数:kwargs和name。
    • regex:regex是正则表达式的通用缩写,它是一种匹配字符串或url地址的语法。Django拿着用户请求的url地址,在urls.py文件中对urlpatterns列表中的每一项条目从头开始进行逐一对比,一旦遇到匹配项,立即执行该条目映射的视图函数或二级路由,其后的条目将不再继续匹配。因此,url路由的编写顺序至关重要!
    • views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
    • kwargs:可选的要传递给视图函数的默认参数(字典形式)
    • name:一个可选的name参数。对你的URL进行命名,可以让你能够在Django的任意处,尤其是模板内显式地引用它。相当于给URL取了个全局变量名,你只需要修改这个全局变量的值,在整个Django中引用它的地方也将同样获得改变。

三、路由规则说明

  • 1.url.py配置基本写法
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]

说明:

  • 1.urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。
  • 2.若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。
  • 3.不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
  • 4.每个正则表达式前面的'r' 是可选的但是建议加上。
  • 5.Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为APPEND_SLASH = True。其作用就是自动在网址结尾加'/'。
    例如我们定义了urls.py:
from django.conf.urls import url
from app1 import views

urlpatterns = [
        url(r'^blog/$', views.blog),
]

访问 http://www.example.com/blog 时,默认将网址自动转换为 http://www.example/com/blog/
如果在settings.py中设置了APPEND_SLASH=False,此时我们再请求 http://www.example.com/blog 时就会提示找不到页面。

四、动态路由

像上述这种例子urlpatterns = [ url(r'^blog/$', views.blog),],其实就是一个静态路由,访问地址是已经固定了,本地请求的话一定要通过访问http://www.127.0.0.1/blog/,这的话局限就比较大,所以就是需要Django匹配正则来做到动态路由。
例,urls.py代码:

from django.conf.urls import url, include
from django.contrib import admin
from cmdb import views

urlpatterns = [

    #####静态路由#####
    # ① 匹配规则  http://127.0.0.1:8000/index/*
    url(r'^index/', views.index),

    #####动态路由--利用正则表达式可以达到分页url的效果#####
    # ② 匹配规则  http://127.0.0.1:8000/detail/432432  将最后面的数字当做参数传递给views.detail函数的nid参数
    url(r'^detail/(\d+)', views.detail),

    # ③ 匹配规则  http://127.0.0.1:8000/detail2/432432/2  将最后面的两个数字当做参数分别传递给views.detail函数的nid和nnid参数
    url(r'^detail2/(\d+)/(\d+)', views.detail2),

    # ④ 匹配规则  http://127.0.0.1:8000/detail3/432432/2  将最后面的两个数字根据自定义的名字当做参数分别传递给views.detail函数的p1和p2参数
    url(r'^detail3/(?P\d+)/(?P\d+)', views.detail3),
    
]

对应的业务函数view.py代码:

from django.shortcuts import render
from django.shortcuts import HttpResponse

# Create your views here.

def index(request):
    return render(request, "index.html")

def detail(request, nid):
    print(nid)
    return HttpResponse("OK")

def detail2(request, nid, nnid):
    print(nid, nnid)
    return HttpResponse("OK")

def detail3(request, p1, p2):
    print(p1, p2)
    return HttpResponse("OK")

补充说明:
为视图函数中指定默认值

# urls.py中
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^blog/$', views.page),
    url(r'^blog/page(?P[0-9]+)/$', views.page),
]

# views.py中,可以为num指定默认值
def page(request, num="1"):
    pass

在上面的例子中,两个URL模式指向相同的view 下面的views.page 但是第一个模式并没有从URL中捕获任何东西。
如果第一个模式匹配上了,page()函数将使用其默认参数num=“1”,如果第二个模式匹配,page()将使用正则表达式捕获到的num值

动态路由的作用
有了动态路由就 可以不用在 url?nid=8这样传值了!
终止符:^edit$ 可以精确限制匹配URL的后缀内容
伪静态:url(r'^edit/(\w+).html$', views.edit) 装作成静态网站的url可以 提高 SEO高权重 百度排名靠前

五、路由分发

如果一个项目下有很多的app,那么在urls.py里面就要写巨多的urls映射关系。这样看起来很不灵活,而且杂乱无章。
因此,我们可以根据不同的app来分类不同的url请求。

方法一(也是最常用的分发)
首先,在urls.py里写入urls映射条目。注意要导入include方法。

#根目录下的url.py
from django.conf.urls import url,include
from django.contrib import admin
from app1 import views
urlpatterns = [
     url(r'^app1/',include('app1.urls')),
     url(r'^app2/',include('app2.urls')),  #注意include的是字符串形式的 文件路径;
     url(r'^',views.error),                
                     ]

这条关系的意思是将url为app1/的请求都交给app1下的urls去处理
其次,在app1下创建一个urls.py文件,用来处理请求的url,使之与views建立映射。

#app1下面的url.py
from django.conf.urls import include, url
from app01 import views

urlpatterns = [
    url(r'index/$', views.index),
]

对于url请求为:http://127.0.0.1/app1/index/

方法二(了解一下)
此方法不需要再导入include,适用于只是少量分发,因为过多url的话会看起来很乱。
格式:

urlpatterns = [
     url(r'正则表达式/', ([
     url(r'正式表达式', 视图方法名),
                          ], None, None)),           
                     ]

看例子:

#根目录下的url.py
from django.conf.urls import url
from django.contrib import admin
from app1 import views
from . import views
urlpatterns = [
     url(r'^app1/', ([
url(r'^test1/', views.test1),
url(r'^test2/', views.test2),
], None, None)),
     url(r'^',views.error),                
                     ]

对于url请求为:http://127.0.0.1/app1/test1/http://127.0.0.1/app1/test2/,这种写法也能实现不断的路由分发,因为([], None,None)是能够一直嵌套下去的。

六、路由的别名和反向解析

Django 提供一个办法是让URL 映射是URL 设计唯一的地方。你填充你的URLconf,然后可以双向使用它:

  • 根据用户/浏览器发起的URL 请求,它调用正确的Django 视图,并从URL 中提取它的参数需要的值。
  • 根据Django 视图的标识和将要传递给它的参数的值,获取与之关联的URL。

第一种方式是我们在前面的章节中一直讨论的用法。第二种方式叫做反向解析URL、反向URL 匹配、反向URL 查询或者简单的URL 反查。
在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:

  • 在模板中:使用url模板标签。
  • 在Python 代码中:使用django.core.urlresolvers.reverse() 函数。
  • 在更高层的与处理Django 模型实例相关的代码中:使用get_absolute_url() 方法。

上面说了一大堆,你可能并没有看懂。(那是官方文档的生硬翻译)。
咱们简单来说就是可以给我们的URL匹配规则起个名字,一个URL匹配模式起一个名字。
这样我们以后就不需要写死URL代码了,只需要通过名字来调用当前的URL。

例:

url(r'^home', views.home, name='home'),  # 给我的url匹配模式起名为 home
url(r'^index/(\d*)', views.index, name='index'),  # 给我的url匹配模式起名为index
url(r'^edit/(?P\w+)/(?P\w+)/',views.edit,name="edit"), 给我的url匹配模式起名为 edit

这样,在模板里面可以这样引用:

{% url 'home' %}
#就是访问/home/

如果url有参数的话应该怎样在模板引用呢?

index
#格式:{% url 'url别名' 空格 传递的参数 %}
#假如有多个参数的话,就继续以空格间隔开就行,如:
index

在视图函数使用 reverse("别名", args=(1, )) 反生 位置参数动态路由的 别名:
注意,这里传入的args是一个元组

from django.urls import reverse

def index(request,n1):
    print(reverse("index", args=(1, )))
#输出:/index/1/
#这里是写死参数1,没有接收n1
    return render(request,'index.html',{'i':1,'i1':2})

在视图函数使用 reverse("别名",kwargs={"关键字参数":1,"关键字参数":2 }反生 关键字参数动态路由的 别名

from django.urls import reverse

def index(request,n1,n2):
    print(reverse("edit",kwargs={"n1":1,"n2":2 }))
#输出:/edit/1/2/
#字典的键必须与url正则起的分组名相同,不然会报错
    return render(request,'index.html',{'i':1,'i1':2})

注意

为了完成上面例子中的URL 反查,你将需要使用命名的URL 模式。URL 的名称使用的字符串可以包含任何你喜欢的字符。不只限制在合法的Python 名称。

当命名你的URL 模式时,请确保使用的名称不会与其它应用中名称冲突。如果你的URL 模式叫做edit,而另外一个应用中也有一个同样的名称,当你在模板中使用这个名称的时候不能保证将插入哪个URL。

在URL 名称中加上一个前缀,比如应用的名称,将减少冲突的可能。我们建议使用myapp-edit 而不是edit

七、小小序幕-Django2.X路由系统与1.X的区别

Django 2.X版本中的路由系统已经替换成下面的写法(官方文档):

from django.urls import path

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    path('articles//', views.year_archive),
    path('articles///', views.month_archive),
    path('articles////', views.article_detail),
]

path参数的使用方法path(‘articles//’) 简单了很多,就是尖括号,前边是int代表参数的类型,后面代表参数的名称
path参数类型:
捕获url中的参数需要用到尖括号<> 指定尖括号中的值类型比如int:str:link这个转换器还有许多类型比如:
int 匹配0和正整数
str 匹配任何空字符串但不包括/
slug 可理解为注释 匹配任何ascii码包括连接线和下划线
uuid 匹配一个uuid对象(该对象必须包括破折号—,所有字母必须小写)
path 匹配所有的字符串 包括/(意思就是path前边和后边的所有)

两个版本基本上来说是大同小异,但也是存在小小区别,如果不注意这些区别就会引起一些不必要的报错,下面就来对比一下两者的区别。

    1. 导入模块的区别
      1.X常用的导入模块
      from django.conf.urls import url, include
      2.X常用的导入模块
      from django.urls import path,include,re_path
    1. 匹配区别
      在1.X的时候,都是采用的url方式:
url(r'^', include("test1.urls")),

在2.0中,它推荐使用的是path模块,所以这里就改写一下。引包from django.urls import path

path('', include("test1.urls")),

这里要注意的是,如果要使用正则,则要引入re_pathfrom django.urls import re_path
使用re_path的话基本与上述的1.X的url正则匹配一致。
例,匹配这个链接http://127.0.0.1:8000/page=12&key=abc,
1.X的写法:

url(r’^page=(?P\d+)&key=(?P\w+)$’, views.detail, name=”detail”), 

2.X的写法:

re_path('page=(?P\d+)&key=(?P\w+)', views.detail, name="detail"),
  • 3.关于系统的urls.py里的namespace的区别
    1.X的写法:
from django.conf.urls import url, include

url(r'^', include("test1.urls", namespace='test1')),

2.X的写法:

from django.urls import path
app_name = 'test1'

path(r'', include("test1.urls", namespace='test1')),

区别在于要先声明app_name,如果不先声明app_name就会找不到这个命名空间报错。

你可能感兴趣的:(Django 1.X的路由系统)