django -- 路由

前戏

在之前的文章中,我们已经知道了什么是路由。路由就是urls.py文件里urlpatterns下写的一个个路径,用户输入路径之后,Django在里面找对应的路径,然后去执行views.py里的函数。前面只是学了最简单的用法,今天来看看还有哪些用法

官方文档

基本的格式:

from django.conf.urls import url

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

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

views视图:一个可调用对象,通常为一个视图函数

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

别名:一个可选的name参数

注意

Django2.xx的路由是下面这样的写法

from django.urls import path,re_path

urlpatterns = [
    path('login', views.login),

]

如果你想把1.xx的升级到2.xx,要把url换成re_path

正则表达式

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/[0-9]{2}/[0-9]{3}', views.login),
]

代码解释:

我们在路径里写了一个正则表达式,匹配两个数字/三个数字,这样就能访问到这个网站了。^表示以什么开头,如果不知道,可以去看看python的re模块

不知道有没有发现,如果你输入两个数字一个反斜杠,后面不管输入几个数字都可以匹配到,这是因为我们没有限制以什么结尾。正则只要匹配到就不再往下查找了,我们可以在最后加个结尾符$

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/[0-9]{2}/[0-9]{3}$', views.login),
]

这样我们的结尾就只能是三位数字

说明:

  • urlpatterns中的元素按照顺序从上往下逐一匹配正则表达式,一旦匹配成功则不在往下匹配
  • 不需要添加一个前面的反斜杠,因为每个URL都有,例如 ^login而不是 ^/login
  • 每个正则表达式前面的‘r’是可选的,但是建议写上
  • Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'。如果不需要,在settings.py配置文件中设置APPEND_SLASH  = FALSE
  • 若要从URL中获取一个值,只需要用正则里的分组匹配

分组匹配

在Python的正则表达式中,分组命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式。

在之前写的删除图书的时候,我们需要获取书的id,当时我们是这样写的,我们获取到id的值后传递给了id

<a href="/delete_book/?id={{ book.id }}">删除</a>

现在我们可以使用分组匹配来改写一下,首先是改写urls.py

原来的路由

url(r'^delete_book/', views.delete_book),  # 删除图书

改成分组匹配的路由

url(r'^delete_book/(\d+)/', views.delete_book),  # 删除图书

对应的html也要改,直接去掉?id=就可以了

<a href="/delete_book/{{ book.id }}">删除</a>

然后我们点击删除,发现页面报错了

报错信息告诉我们需要一个位置参数,但给了两个,那我们去看看对应的函数是怎么写的

#删除图书
def delete_book(request):
    delete_book_id = request.GET.get('id')
    Book.objects.filter(id=delete_book_id).delete()
    return redirect('/book_list/')

函数接收一个参数没错呀,为什么报错信息提示我们给了两个参数?这是因为Django会把分组匹配里的内容当做一个参数传给对应的函数,这样我们就不需要从url里获取书的id值了,函数直接接收就行了,在来修改下我们的函数

def delete_book(request, delete_book_id):
    # delete_book_id = request.GET.get('id')
    Book.objects.filter(id=delete_book_id).delete()
    return redirect('/book_list/')

这样我们就能正常删除了,书的id传给了参数delete_book_id这个参数

除了上面的这种写法,我们还可以给分组起个名称,然后再函数里接收这个参数

在修改URL

url(r'^delete_book/(?P<book_id>\d+)/', views.delete_book),  # 删除图书

在去删除图书,发现页面又报错了

报错信息告诉我们,得到了一个意外的关键字参数book_id,这是因为,如果是分组命名的话,django会把我们起的名称以关键字(字典)的形式传给对应的函数,那我们把之前函数里写的 delete_book_id参数名改为book_id就可以了

#删除图书
def delete_book(request, book_id):
    # delete_book_id = request.GET.get('id')
    Book.objects.filter(id=book_id).delete()
    return redirect('/book_list/')

注意:

URL在匹配的时候,只匹配路径,也就是说?和后面的参数都不匹配

路由不检查请求方式,也就是说,get,post,delete,put等,只要是同一个路径,都会匹配到同一个函数

只要是正则表达式匹配的,获得的参数永远都是字符串(除非你自己转)

 include(路由分发)

之前我们的路由都是写在项目里的urls.py里面,但是我们的项目可能会越来越大,如果都写在一个py文件里,以后找起来也很麻烦,那有没有更好的方法呢?之前说app的时候,我们知道我们可以把一个模块或功能放在一个app里面,那我们可以把对应这个模块的路由也放在对应的app下面,这样我们找起来就很容易了

在之前appTest01的app下创建一个urls.py文件,把相关的路由放到这里面

虽然这样改了,但是查找还是要在项目的urls.py里面进行查找,这时我们在项目里的urls.py里加上下面这句代码,让去appTest01下面去找,记得最后加"/"

from django.conf.urls import url,include


urlpatterns = [
   
    url(r'^appTest01/', include('appTest01.urls')),
]

改了之后,我们对应的请求也要改,要在前面加上app名,如

http://127.0.0.1:8080/appTest01/book_list/

这样我们访问之后,先去项目的urls.py找对应的的appTest01,找到之后,在去include里的urls里面找,这种方法叫路由分发

路由传递参数

在上面说路由的格式的时候,说可以给路由写参数,可以接收一个可选的第三个参数,它是一个字典,表示想要传递给视图函数的额外关键字参数。

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

例如:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^login/', views.login, {'job': 'IT'}), ]

当请求访问login时,会给对应的函数传递一个关键字参数,如果额外传的这个关键字参数和URL中分组命名传的参数同名时,函数则使用额外给的这个参数,而不是URL中的参数

URL反向解析(别名)

我们之前的URL都是写死的,假如有一天,你看着这个URL名不爽了,然后你把这个名词换了一个,这时候你就要把所有引用到的这个URL都要改过来,又过了一段时间,你又觉着不爽。。。

Django提供了我们一种给URL起别名的方式。别名就是给URL起了一个名字,通过这个名字反向拿到了URL地址。以后你如果看URL不爽了,你只需要改路由里的一个就可以了,其他引用的地方都使用这个别名就不需要改了

例子;

url(r'^press_list/', views.press_list, name='press'),  # 给url起了个别名为press

在视图函数里可以这样引用

from django.urls import reverse

def press_list(request):
    ret = Press.objects.all()  
    print(reverse('press'))

 
    return redirect(reverse('press'))  # 通过别名去找对应的URL

结果:

/press_list/

拿到的就是URL前面的路径,所以不管你urls.py里怎么改,这里都不需要改

在模版里这么引用

{% url 'press' %}
分组匹配的别名

之前在写正则表达式的时候,写了正则匹配的一个路由

urlpatterns = [

    url(r'^login/([0-9]{2})/([0-9]{3})', views.login, name='home'),
]

如果是通过正则表达式写的话,则需要传递参数,不传会报错

在views.py里引用

from django.urls import reverse

reverse("home", args=("11","222" ))  # 前面是两个数字,后面是三个数字,随便写

在模版中的引用

{%  url 'home' '22' '333' %}
分组命名的别名

路由:

url(r'^delete_book/(?P<book_id>[0-9]{4})/(?P<press_id>[0-9]{2})/', views.delete_book, name='home')

在视图中的引用有两种方式,一种是上面的方式

reverse("home", args=("2019","07" ))

另一种方式

reverse("home", kwargs={"book_id":"2019","press_id":"07"})

在模版中引用也有两种方式,第一种,数字随便写,只要符合上面的正则表达式就行

{%  url 'home' '2011' '33' %}

另一种:前面的名称要和路由里起的名称一样

{%  url 'home' book_id='2011' press_id='33' %}

命名空间

假如我们有多个app,并且每个app下都有一个别名,name=’home‘

urlpatterns = [

    url(r'^appTest01/', include('appTest01.urls')),
    url(r'^appTest02/', include('appTest02.urls')),
]

appTest01下的和appTest02下的别名都是一样的,如下

url(r'^author_list/', views.author_list, name='home'),

这时我们去打印别名,前面的路径都是appTest02,因为appTest02在下面,把上面的覆盖了

/appTest02/author_list/

这时候我们就需要在项目的urls.py里加上命名空间

urlpatterns = [

    url(r'^login/[0-9]{2}/[0-9]{3}$', views.login),
    url(r'^appTest01/', include('appTest01.urls', namespace='appTest01')),
    url(r'^appTest02/', include('appTest02.urls', namespace='appTest01')),
]

在视图中使用

reverse("appTest01:home", kwargs={"book_id":"2019","press_id":"07"})

或者

reverse("appTest02:home", kwargs={"book_id":"2019","press_id":"07"})

在模版中使用

{%  url 'appTest01:home' '2011' '33' %}

或者

{%  url 'appTest02:home' '2011' '33' %}

 

posted @ 2019-07-19 21:31  邹邹很busy。  阅读(262)  评论(0编辑  收藏  举报