Django——路由基础
Posted lymlike
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django——路由基础相关的知识,希望对你有一定的参考价值。
URL是Web服务的入口,用户通过浏览器发送过来的任何请求,都是发送到一个指定的URL地址,然后被响应。
在Django项目中编写路由,就是向外暴露我们接收哪些URL的请求,除此之外的任何URL都不被处理,也没有返回。通俗地理解,不恰当的形容,URL路由是你的Web服务对外暴露的API。
Django奉行DRY主义,提倡使用简洁、优雅的URL,没有.php
或.cgi
这种后缀,更不会单独使用0、2097、1-1-1928、00这样无意义的东西,让你随心所欲设计你的URL,不受框架束缚。
1.Django如何处理请求
当用户请求一个页面时,Django根据下面的逻辑执行操作:
- 决定要使用的根URLconf模块。通常,这是
ROOT_URLCONF
设置的值,但是如果传入的HttpRequest对象具有urlconf属性(由中间件设置),则其值将被用于代替ROOT_URLCONF
设置。通俗的讲,就是你可以自定义项目入口url是哪个文件! - 加载该模块并寻找可用的urlpatterns。 它是
django.urls.path()
或者django.urls.re_path()
实例的一个列表。 - 依次匹配每个URL模式,在与请求的URL相匹配的第一个模式停下来。也就是说,url匹配是从上往下的短路操作,所以url在列表中的位置非常关键。
- 导入并调用匹配行中给定的视图,该视图是一个简单的Python函数(被称为视图函数),或基于类的视图。 视图将获得如下参数:
- 一个HttpRequest 实例。
- 如果匹配的表达式返回了未命名的组,那么匹配的内容将作为位置参数提供给视图。
- 关键字参数由表达式匹配的命名组组成,但是可以被
django.urls.path()
的可选参数kwargs覆盖。
- 如果没有匹配到任何表达式,或者过程中抛出异常,将调用一个适当的错误处理视图。
2.URL的正向解析
上官方文档代码
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:slug>/‘, views.article_detail), ]
注意:
- 代码中的尖括号,在Django中尖括号的作用是捕获值,“:”之前式转换器的类型,后面是转换器捕获值的名称。此处应当提前说明一下,此处捕获的参数会被传入对应的views函数里面,假使函数没有声名这个参数,就会报错;
- 可以转换捕获到的值为指定类型,比如例子中的int。默认情况下,捕获到的结果保存为字符串类型,不包含
/
这个特殊字符; - 匹配模式的最开头不需要添加
/
,因为默认情况下,每个url都带一个最前面的/
,既然大家都有的部分,就不用浪费时间特别写一个了。
2.1匹配例子
- /articles/2005/03/ 将匹配第三条,并调用views.month_archive(request, year=2005, month=3);
- /articles/2003/匹配第一条,并调用views.special_case_2003(request);
- /articles/2003将一条都匹配不上,因为它最后少了一个斜杠,而列表中的所有模式中都以斜杠结尾;
- /articles/2003/03/building-a-django-site/ 将匹配最后一个,并调用views.article_detail(request, year=2003, month=3, slug="building-a-django-site"
2.2路径转换器
默认情况下,Django内置下面的路径转换器:
- str:匹配任何非空字符串,但不含斜杠
/
,如果你没有专门指定转换器,那么这个是默认使用的; - int:匹配0和正整数,返回一个int类型
- slug:可理解为注释、后缀、附属等概念,是url拖在最后的一部分解释性字符。该转换器匹配任何ASCII字符以及连接符和下划线,比如’ building-your-1st-django-site‘;
- uuid:匹配一个uuid格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写,例如’075194d3-6885-417e-a8a8-6c931e272f00‘ 。返回一个UUID对象;
- path:匹配任何非空字符串,重点是可以包含路径分隔符’/‘。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串。
2.3使用正则表达式
如果路径和转换器语法不足以定义URL模式,则还可以使用正则表达式。为此,请使用 re_path()而不是path()。
在Python正则表达式中,命名正则表达式组的语法是(?P<name>pattern),name是匹配的字符串的名称,并且 pattern是要匹配的模式。
对前面的示例URLconf使用正则表达式重写:
from django.urls import path, re_path from . import views urlpatterns = [ path(‘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<slug>[\w-]+)/$‘, views.article_detail), ]
这完成了与前一个示例大致相同的事情,除了:
- 匹配的确切网址稍微受限制。例如,年份10000将不再匹配,因为年份整数被限制为恰好四位数。
- 无论正则表达式匹配什么类型,每个捕获的参数都将作为字符串发送到视图。
除了命名组语法之外,例如(?P<year>[0-9]4),还可以使用较短的未命名组,例如([0-9]4),不推荐这种用法,不再赘述。
2.4指定视图参数的默认值
有一个小技巧,可以指定视图参数的默认值。 下面是一个URLconf和视图的示例:
# URLconf from django.urls import path from . import views urlpatterns = [ path(‘blog/‘, views.page), path(‘blog/page<int:num>/‘, views.page), ] # views.py def page(request, num=1): # Output the appropriate page of blog entries, according to num. ...
在上面的例子中,两个URL模式指向同一个视图views.page
。但是第一个模式不会从URL中捕获任何值。 如果第一个模式匹配,page()函数将使用num参数的默认值"1"。 如果第二个模式匹配,page()将使用捕获的num值。
2.5路由转发(include)
在一般情况下,进行网站的编写,url不可能简简单单的只有几个,而应该是很多,此时如果将需要匹配的url全部放到urlpattern里面,肯那个就就会显得有些冗余。include函数就应运而生了。每当Django遇到时include(),它会去掉URL中已经匹配的部分,并将剩余的字符串发送到包含的URLconf以进行进一步处理,也就是转发到二级路由去。
被包含的URLconf会收到来自父URLconf捕获的任何参数##
from django.urls import include, path urlpatterns = [ # ... path(‘community/‘, include(‘aggregator.urls‘)), path(‘contact/‘, include(‘contact.urls‘)), # ... ]
上面的url,community/就会跳转到aggregator.urls内部进行继续匹配。
再看一个例子:
from django.conf.urls import url from . import views urlpatterns = [ re_path(r‘^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/history/$‘, views.history), re_path(r‘^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/edit/$‘, views.edit), re_path(r‘^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/discuss/$‘, views.discuss), re_path(r‘^(?P<page_slug>[\w-]+)-(?P<page_id>\w+)/permissions/$‘, views.permissions), ]
上面的路由写得不好,我们可以改进它,只需要声明共同的路径前缀一次,并将后面的部分分组转发:
from django.urls import include, path from . import views urlpatterns = [ path(‘<page_slug>-<page_id>/‘, include([ path(‘history/‘, views.history), path(‘edit/‘, views.edit), path(‘discuss/‘, views.discuss), path(‘permissions/‘, views.permissions), ])), ]
2.6传递额外参数
URLconfs允许我们将额外的参数作为Python字典传递给视图函数。
该path()函数可以采用可选的第三个参数,该参数应该是传递给视图函数的额外关键字参数的字典。例如:
from django.urls import path from . import views urlpatterns = [ path(‘blog/<int:year>/‘, views.year_archive, ‘foo‘: ‘bar‘), ]
在这个例子中,对于请求/blog/2005/,Django将调用 。views.year_archive(request, year=2005, foo=‘bar‘)
同样,我们可以传递额外的选项,include()并且包含的??URLconf中的每一行都将传递额外的选项。无论是传递给当前的url文件还是include指向的文件,都是被允许的。
3.URL的反向解析
3.1反向解析URL
反向解析url主要是为了解决url硬性编码的问题,硬性编码不仅费时、不可扩展、难以维护、而且很容易出错。
Django提供了一种解决方案,只需在URL中提供一个name参数。
通过这个name参数,可以反向解析URL、反向URL匹配、反向URL查询或者简单的URL反查。
在需要解析URL的地方,对于不同层级,Django提供了不同的工具用于URL反查:
-
在模板语言中:使用
url
模板标签。(也就是写前端网页时) -
在Python代码中:使用
reverse()
函数。(也就是写视图函数等情况时) -
在更高层的与处理Django模型实例相关的代码中:使用
get_absolute_url()
方法。(也就是在模型model中)
搬个文档例子:
# urls.py from django.urls import path from . import views urlpatterns = [ #... path(‘articles/<int:year>/‘, views.year_archive, name=‘news-year-archive‘), #... ]
在模板中
<a href="% url ‘news-year-archive‘ 2012 %">2012 Archive</a> <ul> % for yearvar in year_list % <li><a href="% url ‘news-year-archive‘ yearvar %"> yearvar Archive</a></li> % endfor % </ul>
在python代码中
from django.http import HttpResponseRedirect from django.urls import reverse def redirect_to_year(request): # ... year = 2019 # ... return HttpResponseRedirect(reverse(‘news-year-archive‘, args=(year,)))
其中,起到核心作用的是我们通过name=‘news-year-archive‘
为那条url起了一个可以被引用的名称。
4.URL的命名空间
先看下这个命名空间是干嘛用的?
上面我们解决了url的硬性编码问题,就是给url起个别名,我们知道一个项目可能会包含多个app应用,所以除非你列一个很清晰的表格记录分别给每个app的每个url起了什么别名,以确保他们的名字没有重复,否则我们很难记得是不是已经有了一个叫“张伟”的。如果不同app的url起了相同的名字,那就要看谁排在前面了,这种抽奖式方式显然不是我们需要的,为了解决这个问题,就有了命名空间。
URL命名空间可以保证反查到唯一的URL,即使不同的app使用相同的URL名称。
实现命名空间的做法很简单,在urlconf文件中添加app_name = ‘polls‘
和namespace=‘author-polls‘
这种类似的定义。
简单例子:
project的urls.py
urlpatterns = [ path(‘admin/‘, admin.site.urls), path(‘app01/‘, include("app01.urls",namespace="app01")), path(‘app02/‘, include("app02.urls",namespace="app02")), ]
app01的urls.py
urlpatterns = [ path(‘index/‘, views.index,name="index"), ]
app02的urls.py
urlpatterns = [ path(‘index/‘, views.index,name="index"), ]
app01的views.py
from django.core.urlresolvers import reverse def index(request): print(‘app01‘) return HttpResponse(reverse("app01:index"))
app02的views.py
from django.core.urlresolvers import reverse def index(request): print(‘app02‘) return HttpResponse(reverse("app02:index"))
以上是关于Django——路由基础的主要内容,如果未能解决你的问题,请参考以下文章