django路由层 分组 反向解析 路由分发 名称空间 re_path与path

路由的作用

路由即请求地址与视图函数的映射关系,如果把网站比喻为一本书,那路由就好比是这本书的目录,在Django中路由默认配置在urls.py中

路由配置

from django.conf.urls import url
​
urlpatterns = [
     url(正则表达式, views视图函数,参数,name=别名),  # 参数,别名可不写
]

1. url()方法,第一个参数其实是一个正则表达式,一旦前面的正则匹配到了内容,就不会再往下继续匹配,而是直接执行对应的视图函数
正是由于此特性,当你的项目特别庞大的时候,url的前后顺序也是你需要你考虑极有可能会出现url错乱的情况

2. django在路由匹配的时候,当你在浏览器中没有敲最后的斜杠,django会先拿着你没有敲斜杠的结果取匹配,如果都没有匹配上,
会让浏览器在末尾加斜杠再发一次请求,再匹配一次,如果还匹配不上才会报错

取消该机制:

# 在settings.py文件中:
APPEND_SLASH = False  # 该参数默认是True,其作用就是自动在网址结尾加'/'

补充:

# 匹配主页
url(r'^$',views.home),
# 不存在的页面
url(r'',views.error),

分组

什么是分组、为何要分组呢?比如我们开发了一个博客系统,当我们需要根据文章的id查看指定文章时,浏览器在发送请求时需要向后台传递参数(文章的id号),可以使用 http://127.0.0.1:8001/article/?id=3,也可以直接将参数放到路径中http://127.0.0.1:8001/article/3/

针对后一种方式Django就需要直接从路径中取出参数,这就用到了正则表达式的分组功能了,分组分为两种:无名分组与有名分组

  • 无名分组

路由匹配的时候,会将括号内正则表达式匹配到的内容 当做位置参数传递给视图函数

urls.py文件:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    
    # 下述正则表达式会匹配url地址的路径部分为:article/数字/,匹配成功的分组部分会以位置参数的形式传给视图函数,有几个分组就传几个位置参数
    url(r'^aritcle/(\d+)/$',views.article), 
]

views.py文件:

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

# 需要额外增加一个形参用于接收传递过来的分组数据
def article(request,id):
    return HttpResponse('id为 %s 的文章内容...' %id)
  • 有名分组

路由匹配的时候,会将括号内正则表达式匹配到的内容 当做关键字参数传递给视图函数

urls.py文件:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    
    # 该正则会匹配url地址的路径部分为:article/数字/,匹配成功的分组部分会以关键字参数(article_id=匹配成功的数字)的形式传给视图函数,有几个有名分组就会传几个关键字参数
    url(r'^aritcle/(?P<article_id>\d+)/$',views.article), 
]

views.py文件:

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

# 需要额外增加一个形参,形参名必须为article_id
def article(request,article_id):
    return HttpResponse('id为 %s 的文章内容...' %article_id)

总结:有名分组和无名分组都是为了获取路径中的参数,并传递给视图函数,区别在于无名分组是以位置参数的形式传递,有名分组是以关键字参数的形式传递。

强调:无名分组和有名分组不要混合使用

url(r'^test/(?P<kwargs>\d+)/(\d+)$', views.test),  # 报错

但是用一种分组下 可以使用多个

# 无名分组支持多个
url(r'^test/(\d+)/(\d+)/', views.test),
def test(request, *args):
    print(args)  >>> ('12123', '123')
    return HttpResponse('test')
​
# 有名分组支持多个
url(r'^test/(?P<kwargs>\d+)/(?P<xx>\d+)/', views.test),
def test(request, kwargs, xx):
    print(kwargs)
    print(xx)
    return HttpResponse('test')

1.在更高级的用法中,可以使用分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图。
2.在Python的正则表达式中,分组命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式。
3.每个在URLconf中捕获的参数都作为一个普通的Python字符串传递给视图,无论正则表达式使用的是什么匹配方式

反向解析

本质:其实就是给你返回一个能够对应url的地址,通过这个rul触发对应的视图函数

在软件开发初期,url地址的路径设计可能并不完美,后期需要进行调整,如果项目中很多地方使用了该路径,一旦该路径发生变化,就意味着所有使用该路径的地方都需要进行修改,这是一个非常繁琐的操作。

解决方案就是在编写一条url(regex, view, kwargs=None, name=None)时,可以通过参数name为url地址的路径部分起一个别名,项目中就可以通过别名来获取这个路径。以后无论路径如何变化别名与路径始终保持一致。

上述方案中通过别名获取路径的过程称为反向解析

使用

urls.py文件中:

url(r'^index/$',views.index,name='kkk')

views.py中:

from django.shortcuts import reverse

def index(request):
    #后端反向解析 后端可以在任意位置通过reverse反向解析出对应的url
    print(reverse('kkk'))  # /index/
    return HttpResponse('index')

.html文件:

#前端反向解析
{% url 'kkk' %}

无名分组反向解析

url(r'^index/(\d+)/$',views.index, name='kkk'),
# 后端反向解析
def index(request, xx):
    print(reverse('kkk',args=(xx,)))  # /index/xx 后面xx通常都是数据的id值
    return render(request, 'login.html')
# 前端反向解析
<a href="{% url 'kkk' 1%}">11</a>  # 后面数字通常都是数据的id值
​
# 一般配合前端模板语法使用
{% for user_obj in user_list %}
    <a href='{{ url 'kkk' user_obj.pk }}/'>按钮</a>
{% endfor %}

有名分组反向解析

url(r'^index/(?P<year>\d+)/$',views.index,name='kkk')
# 后端反向解析
print(reverse('kkk',args=(1,)))  # 推荐你使用上面这种  减少你的脑容量消耗
print(reverse('kkk',kwargs={'year':1}))
# 前端反向解析    
<a href="{% url 'kkk' 1 %}">1</a>  # 推荐你使用上面这种  减少你的脑容量消耗
<a href="{% url 'kkk' year=1 %}">1</a>

路由分发

为什么要用路由分发

1.当你的django项目特别庞大的时候,路由与视图函数对应关系特别特别多.那么你的总路由urls.py代码太过冗长,不易维护
2.每一个应用都可以有自己的urls.py,static文件夹,templates文件夹(******)
3.正是基于上述条件,可以实现多人分组开发,等多人开发完成之后,我们只需要创建一个空的django项目,然后将多人开发的app全部注册进来,在总路由实现一个路由分发,而不再做路由匹配(来了之后,我只给你分发到对应的app中)
4.当你的应用下的视图函数特别特别多的时候  你可以建一个views文件夹 里面根据功能的细分再建不同的py文件(******)

使用

1.在应用下新建urls.py文件,在该文件内写路由与视图函数的对应关系即可

from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r'^index/',views.index)
    ...
]

2.在总路由文件内导入 include

from django.conf.urls import url,include

3.在总路由文件内编写应用名与应用路由对应关系

方法一:
        from app01 import urls as app01_urls # 给url重命名
        from app02 import urls as app02_urls
        urlpatterns = [
            url(r'^app01/',include(app01_urls)),
            url(r'^app02/',include(app02_urls)),
            ...
        ]
方法二:  # 推荐使用
        urlpatterns = [
            url(r'^app01/',include('app01.urls')),
            url(r'^app02/',include('app02.urls')),
            ...
        ]

注意: 总路由中,一级路由(应用名)的后面千万不加$符号

名称空间

路由分发后,多个app中的url起了相同的别名,这个时候用反向解析并不会自动识别应用前缀,如果想避免这种问题的发生,就需要使用

使用

  • 方式一:
# 总路由
url(r'^app01/',include('app01.urls',namespace='app01'))
url(r'^app02/',include('app02.urls',namespace='app02'))
    
# 后端解析的时候
reverse('app01:index')
reverse('app02:index')
# 前端解析的时候
{% url 'app01:index' %}
{% url 'app02:index' %}
  • 方式二:推荐

起别名的时候不要冲突即可 ,一般情况下在起别名的时候通常建议以应用名作为前缀

url(r'^index/(\d+)/', views.index, name='app01_kkk'),
url(r'^index/(\d+)/', views.index, name='app02_kkk'),
​
# 后端解析的时候
reverse('app01_index')
reverse('app02_index')
# 前端解析的时候
{% url 'app01_index' %}
{% url 'app02_index' %}

django2.x版的re_path与path

路由层1.X用的是url
而2.X用的是path
2.X中的path第一个参数不再是正则表达式,而是写什么就匹配什么 是精准匹配
当你使用2.X不习惯的时候  2.X还有一个叫re_path
2.x中的re_path就是你1.X的url
虽然2.X中path不支持正则表达式  但是它提供了五种默认的转换器
1.0版本的url和2.0版本的re_path分组出来的数据都是字符串类型
默认有五个转换器,感兴趣的自己可以去试一下
    str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
    int,匹配正整数,包含0。
    slug,匹配字母、数字以及横杠、下划线组成的字符串。
    uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
    path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
            
path('index/<int:id>/',index)  # 会将id匹配到的内容自动转换成整型
还支持自定义转换器

案例:

urls.py文件

from django.urls import re_path

from app01 import views

urlpatterns = [
    # 问题一:数据类型转换
    # 正则表达式会将请求路径中的年份匹配成功然后以str类型传递函数year_archive,在函数year_archive中如果想以int类型的格式处理年份,则必须进行数据类型转换
    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

    # 问题二:正则表达式冗余
    # 下述三个路由中匹配article_id采用了同样的正则表达式,重复编写了三遍,存在冗余问题,并且极不容易管理,因为一旦article_id规则需要改变,则必须同时修改三处代码
    
    re_path(r'^article/(?P<article_id>[a-zA-Z0-9]+)/detail/$', views.detail_view),
    re_path(r'^articles/(?P<article_id>[a-zA-Z0-9]+)/edit/$', views.edit_view),
    re_path(r'^articles/(?P<article_id>[a-zA-Z0-9]+)/delete/$', views.delete_view),
]
views.py文件
from django.shortcuts import render,HttpResponse

# Create your views here.
def year_archive(request,year):
    print(year,type(year))
    return HttpResponse('year_archive page')

def detail_view(request,article_id):
    print(article_id, type(article_id))
    return HttpResponse('detail_view page')

def edit_view(request,article_id):
    print(article_id, type(article_id))
    return HttpResponse('edit_view page')

def delete_view(request,article_id):
    print(article_id, type(article_id))
    return HttpResponse('delete_view page')

Django2.0中的path如何解决上述两个问题的呢?请看示例

from django.urls import path,re_path

from app01 import views

urlpatterns = [
    # 问题一的解决方案:
    path('articles/<int:year>/', views.year_archive), # <int:year>相当于一个有名分组,其中int是django提供的转换器,相当于正则表达式,专门用于匹配数字类型,而year则是我们为有名分组命的名,并且int会将匹配成功的结果转换成整型后按照格式(year=整型值)传给函数year_archive


    # 问题二解决方法:用一个int转换器可以替代多处正则表达式
    path('articles/<int:article_id>/detail/', views.detail_view), 
    path('articles/<int:article_id>/edit/', views.edit_view),
    path('articles/<int:article_id>/delete/', views.delete_view),
]
#1、path与re_path或者1.0中的url的不同之处是,传给path的第一个参数不再是正则表达式,而是一个完全匹配的路径,相同之处是第一个参数中的匹配字符均无需加前导斜杠

#2、使用尖括号(<>)从url中捕获值,相当于有名分组

#3、<>中可以包含一个转化器类型(converter type),比如使用 <int:name> 使用了转换器int。若果没有转化器,将匹配任何字符串,当然也包括了 / 字符

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值