Django路由层URLconf作用及原理解析


Posted in Python onSeptember 24, 2020

一、Django中路由的作用

URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表。

你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。

from django.conf.urls import url

urlpatterns = [
   url(正则表达式, views视图函数,参数,别名),
]

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

from django.urls import path

urlpatterns = [
  path('articles/2003/', views.special_case_2003),
  path('articles/<int:year>/', views.year_archive),
  path('articles/<int:year>/<int:month>/', views.month_archive),
  path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]

url是个函数,有四个参数,第一个参数要传正则表达式,第二参数传函数内存地址,第三个参数传默认参数,第四个是路由的别名

正则表达式:一个正则表达式字符串#

views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串#

参数:可选的要传递给视图函数的默认参数(字典形式)

别名:一个可选的name参数

简单的路由配置

from django.urls import path,re_path

from app01 import views

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

1、urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。

2、若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。

3、不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。

4、每个正则表达式前面的'r' 是可选的但是建议加上。

一些请求的例子

注意:

# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项

APPEND_SLASH=True

Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'。

其效果就是:

我们定义了urls.py:

from django.conf.urls import url
from app01 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 时就会提示找不到页面。

二、路由的分组

上面的示例使用简单的、没有命名的正则表达式组(通过圆括号)来捕获URL 中的值并以位置 参数传递给视图。在更高级的用法中,可以使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。 在Python 正则表达式中,命名正则表达式组的语法是(?Ppattern),其中name 是组的名称,pattern 是要匹配的模式。 下面是以上URLconf 使用命名组的重写:

from django.urls import path,re_path

from app01 import views

urlpatterns = [
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。例如:

/articles/2005/03/ 请求将调用views.month_archive(request, year='2005', month='03')函数,而不是views.month_archive(request, '2005', '03')。
/articles/2003/03/03/ 请求将调用函数views.article_detail(request, year='2003', month='03', day='03')。
conclusion:

路由的无名分组

url(r'^test2/(\d+)/(\w+)$', views.test2),

无名分组分出几个值,视图函数就要接受几个值(位置参数形式传过来的)

路由的有名分组

url(r'^test3/(?P<id>\d+)/(?P<name>\w+)$', views.test3),

有名分组分出几个值,视图函数就要接受几个值(按关键字传入,位置可以打乱)

有名和无名不能混着用

三、路由分发

Django1.1版本的分发

from django.conf.urls import url,include

#主urls
from django.urls import path,re_path,include
from app01 import views
from app01 import urls
urlpatterns = [ 
# re_path(r'^app01/',include('app01.urls')),#行

# re_path(r'^app01/&',include('app01.urls')),#不行

# path('app01/',include('app01.urls')),#行 

#path('app01/', include(urls)),
]

在app01里创建一个urls

from django.urls import path,re_path
from app01 import views
urlpatterns = [
  re_path(r'^test/(?P<year>[0-9]{2})/$',views.url_test),
]

四、反向解析

在使用Django 项目时,一个常见的需求是获得URL 的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。人们强烈希望不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 毫不相关的专门的URL 生成机制,因为这样容易导致一定程度上产生过期的URL。

在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:

在模板中:使用url 模板标签。

在Python 代码中:使用from django.urls import reverse()函数

urls.py

from django.urls import path,re_path
from app01 import views
urlpatterns = [
  re_path(r'^test/(?P<year>[0-9]{2})/(?P<month>[0-9]{2})/$',views.url_test,name='test'),
]

html

<a href="{% url 'test' 10 23 %}" rel="external nofollow" >哈哈</a>

视图函数中:

from django.shortcuts import render, HttpResponse,redirect,reverse
def url_test(request,year,month):
  print(year)
  print(month)
  url=reverse('test',args=(10,20))
  print(url)
  return HttpResponse('ok')

conclusion:

1 在html代码里{% url "别名" 参数 参数%}

2 在视图函数中:

2.1 url=reverse('test')

2.2 url=reverse('test',args=(10,20))

当命名你的URL 模式时,请确保使用的名称不会与其它应用中名称冲突。如果你的URL 模式叫做comment,而另外一个应用中也有一个同样的名称,当你在模板中使用这个名称的时候不能保证将插入哪个URL。在URL 名称中加上一个前缀,比如应用的名称,将减少冲突的可能。我们建议使用myapp-comment 而不是comment。

五、名称空间

命名空间(英语:Namespace)是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其它命名空间中。 由于name没有作用域,Django在反解URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,立即返回 我们在开发项目时,会经常使用name属性反解出URL,当不小心在不同的app的urls中定义相同的name时,可能会导致URL反解错误,为了避免这种事情发生,引入了命名空间。

project的urls.py:

urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^app01/', include("app01.urls",namespace="app01")),
re_path(r'^app02/', include("app02.urls",namespace="app02")),
]

app01.urls:

urlpatterns = [
re_path(r'^index/', index,name="index"),
]

app02.urls:

urlpatterns = [
re_path(r'^index/', index,name="index"),
]

app01.views

from django.core.urlresolvers import reverse
def index(request):
return HttpResponse(reverse("app01:index"))

app02.views

from django.core.urlresolvers import reverse
def index(request):
return HttpResponse(reverse("app02:index"))

六、django2.0版的path

django2.0的re_path和1.0的url一样

思考情况如下:

urlpatterns = [ 
  re_path('articles/(?P<year>[0-9]{4})/', year_archive), 
  re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view), 
  re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view), 
  re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view), 
]

考虑下这样的两个问题:

第一个问题,函数 year_archive 中year参数是字符串类型的,因此需要先转化为整数类型的变量值,当然year=int(year) 不会有诸如如TypeError或者ValueError的异常。那么有没有一种方法,在url中,使得这一转化步骤可以由Django自动完成?

第二个问题,三个路由中article_id都是同样的正则表达式,但是你需要写三遍,当之后article_id规则改变后,需要同时修改三处代码,那么有没有一种方法,只需修改一处即可?

在Django2.0中,可以使用 path 解决以上的两个问题。

基本示例

这是一个简单的例子:

from django.urls import path 
from . import views 
urlpatterns = [ 
  path('articles/2003/', views.special_case_2003), 
  path('articles/<int:year>/', views.year_archive), 
  path('articles/<int:year>/<int:month>/', views.month_archive), 
  path('articles/<int:year>/<int:month>/<slug>/', views.article_detail), 
# path才支持,re_path不支持

path('order/<int:year>',views.order),
]

基本规则:

  • 使用尖括号(<>)从url中捕获值。
  • 捕获值中可以包含一个转化器类型(converter type),比如使用 <int:name> 捕获一个整数变量。若果没有转化器,将匹配任何字符串,当然也包括了 / 字符。
  • 无需添加前导斜杠。

以下是根据 2.0官方文档 而整理的示例分析表:(跟上面url的匹配关系)

path转化器

文档原文是Path converters,暂且翻译为转化器。

Django默认支持以下5个转化器:

  • str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
  • int,匹配正整数,包含0。
  • slug,匹配字母、数字以及横杠、下划线组成的字符串。
  • uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
  • path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)

注册自定义转化器

对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:

  • regex 类属性,字符串类型
  • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
  • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。

比如:

class FourDigitYearConverter: 
  regex = '[0-9]{4}' 
  def to_python(self, value): 
    return int(value) 
  def to_url(self, value): 
    return '%04d' % value

使用register_converter 将其注册到URL配置中:

from django.urls import register_converter, path 
from . import converters, views 
register_converter(converters.FourDigitYearConverter, 'yyyy') 
urlpatterns = [ 
  path('articles/2003/', views.special_case_2003), 
  path('articles/<yyyy:year>/', views.year_archive), 
  ... 
]

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python里隐藏的“禅”
Jun 16 Python
Python中类的定义、继承及使用对象实例详解
Apr 30 Python
python判断字符串编码的简单实现方法(使用chardet)
Jul 01 Python
微信 用脚本查看是否被微信好友删除
Oct 28 Python
分析python切片原理和方法
Dec 19 Python
python基于twisted框架编写简单聊天室
Jan 02 Python
Python图像处理库PIL的ImageFont模块使用介绍
Feb 26 Python
使用python的turtle函数绘制一个滑稽表情
Feb 28 Python
Python range与enumerate函数区别解析
Feb 28 Python
python字典和json.dumps()的遇到的坑分析
Mar 11 Python
Django nginx配置实现过程详解
Sep 10 Python
Django与AJAX实现网页动态数据显示的示例代码
Feb 24 Python
Python xlrd/xlwt 创建excel文件及常用操作
Sep 24 #Python
安装并免费使用Pycharm专业版(学生/教师)
Sep 24 #Python
Pycharm学生免费专业版安装教程的方法步骤
Sep 24 #Python
python 多线程共享全局变量的优劣
Sep 24 #Python
改变 Python 中线程执行顺序的方法
Sep 24 #Python
浅析Python 字符编码与文件处理
Sep 24 #Python
学生如何注册Pycharm专业版以及pycharm的安装
Sep 24 #Python
You might like
分享PHP header函数使用教程
2013/09/05 PHP
CodeIgniter多语言实现方法详解
2016/01/20 PHP
Laravel 创建指定表 migrate的例子
2019/10/09 PHP
JS小游戏之象棋暗棋源码详解
2014/09/25 Javascript
浅谈JSON中stringify 函数、toJosn函数和parse函数
2015/01/26 Javascript
浅谈javascript函数式编程
2015/09/06 Javascript
javascript获取wx.config内部字段解决微信分享
2016/03/09 Javascript
基于jQuery ligerUI实现分页样式
2016/09/18 Javascript
JavaScript 继承详解(五)
2016/10/11 Javascript
使用Javascript监控前端相关数据的代码
2016/10/27 Javascript
详解JS对象封装的常用方式
2016/12/30 Javascript
javascript回调函数的概念理解与用法分析
2017/05/27 Javascript
vue开发调试神器vue-devtools使用详解
2017/07/13 Javascript
浅谈Node.js爬虫之网页请求模块
2018/01/11 Javascript
JavaScript实现写入文件到本地的方法【基于FileSaver.js插件】
2018/03/15 Javascript
JS集合set类的实现与使用方法示例
2019/02/01 Javascript
JQuery中DOM节点的操作与访问方法实例分析
2019/12/23 jQuery
[49:20]2014 DOTA2国际邀请赛中国区预选赛5.21 CIS VS TongFu
2014/05/22 DOTA
[00:47]DOTA2荣耀之路6:天火,天火!
2018/05/30 DOTA
Python中的异常处理学习笔记
2015/01/28 Python
Python中的探索性数据分析(功能式)
2017/12/22 Python
基于pandas数据样本行列选取的方法
2018/04/20 Python
Python实现去除列表中重复元素的方法小结【4种方法】
2018/04/27 Python
python中itertools模块zip_longest函数详解
2018/06/12 Python
Django3.0 异步通信初体验(小结)
2019/12/04 Python
解析PyCharm Python运行权限问题
2020/01/08 Python
在Ubuntu 20.04中安装Pycharm 2020.1的图文教程
2020/04/30 Python
世界领先的电子书网站:eBooks.com(在线购买小说、非小说和教科书)
2019/03/30 全球购物
Helly Hansen工作服美国官方网上商店:为最恶劣的环境
2019/09/04 全球购物
文明礼仪伴我行演讲稿
2014/05/12 职场文书
幼儿园社区活动总结
2014/07/07 职场文书
文明旅游倡议书
2015/04/28 职场文书
教师远程研修感悟
2015/11/18 职场文书
如何做好员工培训计划?
2019/07/09 职场文书
2019公司借款合同范本2篇!
2019/07/24 职场文书
vue项目打包后路由错误的解决方法
2022/04/13 Vue.js