一、前言
在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<p1>\d+)/(?P<p2>\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<num>[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<n1>\w+)/(?P<n2>\w+)/',views.edit,name="edit"), 给我的url匹配模式起名为 edit
这样,在模板里面可以这样引用:
{% url 'home' %}
#就是访问/home/
如果url有参数的话应该怎样在模板引用呢?
<a href="{% url 'index' 2012 %}">index</a>
#格式:{% url 'url别名' 空格 传递的参数 %}
#假如有多个参数的话,就继续以空格间隔开就行,如:
<a href="{% url 'edit' 2012 2018 %}">index</a>
在视图函数使用 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/<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),
]
path参数的使用方法path(‘articles/<int:year>/’) 简单了很多,就是尖括号,前边是int代表参数的类型,后面代表参数的名称
path参数类型:
捕获url中的参数需要用到尖括号<> 指定尖括号中的值类型比如int:str:link
这个转换器还有许多类型比如:
int
匹配0和正整数
str
匹配任何空字符串但不包括/
slug
可理解为注释 匹配任何ascii码包括连接线和下划线
uuid
匹配一个uuid对象(该对象必须包括破折号—,所有字母必须小写)
path
匹配所有的字符串 包括/(意思就是path前边和后边的所有)
两个版本基本上来说是大同小异,但也是存在小小区别,如果不注意这些区别就会引起一些不必要的报错,下面就来对比一下两者的区别。
-
导入模块的区别
1.X常用的导入模块
from django.conf.urls import url, include
2.X常用的导入模块
from django.urls import path,include,re_path
-
导入模块的区别
-
匹配区别
在1.X的时候,都是采用的url方式:
-
匹配区别
url(r'^', include("test1.urls")),
在2.0中,它推荐使用的是path模块,所以这里就改写一下。引包from django.urls import path
path('', include("test1.urls")),
这里要注意的是,如果要使用正则,则要引入re_path
,from 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<page>\d+)&key=(?P<key>\w+)$’, views.detail, name=”detail”),
2.X的写法:
re_path('page=(?P<page>\d+)&key=(?P<key>\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
就会找不到这个命名空间报错。