zoukankan      html  css  js  c++  java
  • django路由系统

    django路由系统

    为了替应用程序设计对应的urls, 我们创建了一个URLConf(url configuration)的模块. URL配置就像一个字典的目录一样, 让我们的应用程序(视图函数)和对应的url(本质是正则表达式)形成映射关系. 以这种方式, 用户访问哪个url, django就能到正确的映射函数里去执行了.

    url基本配置

    django 1.11版本的路由匹配

    from django.conf.urls import url
    
    urlpatterns = [
         url(正则表达式, views视图函数,参数,别名),
    ]
    
    • 正则表达式: 正则表达式模式串

    • views视图函数: 一个callable对象, 指向执行具体业务逻辑代码的函数

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

    • 一个可选的name参数, 反向解析时需要使用

    下面是Django 1.11 文档中的典型例子.

    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),
    ]
    

    注意事项:

    • 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
    • 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles
    • 每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不应该转义
    • urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续

    django 2.0版本的路由配置

    Django2.0版本以上的路由配置与1.0+版本的路由配置有了一些变化.

    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),
    ]
    

    注意事项:

    • 使用尖括号(<>)从url中捕获值
    • 捕获值中可以包含一个转化器类型(converter type),比如使用 <int:name> 捕获一个整数变量, 后面的视图函数的参数的名字也要与之对应. 若果没有转化器,将匹配任何字符串,当然也包括了 / 字符.
    • 无需添加前导斜杠
    • 默认的已经是使用path来做路由匹配了.
    • 经过转换器后转换后的类型可能也已经变为相应的数据类型了

    默认的path转换器

    • str:非空的字符串类型。默认的转换器。但是不能包含斜杠;
    • int:匹配任意的零或者正数的整形。到视图函数中就是一个int类型;
    • slug:由英文中的横杠 - ,或者下划线 _ 连接英文字符或者数字而成的字符串;
    • uuid:匹配 uuid 字符串;
    • path:匹配非空的英文字符串,可以包含斜杠;

    自定义转换器

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

    • regex 类属性,字符串类型

    • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。

    • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。

    from django.urls import path, register_converter
    class FourDigitYearConverter:
        regex = '[0-9]{4}'
    
        def to_python(self, value):
            """直接转换, 传递给对应的视图函数中作为参数"""
            return int(value)
    
        def to_url(self, value):
            return '%04d' % value
    
    
    # 注册自定义的转换器
    register_converter(FourDigitYearConverter, 'yyyy')
    
    urlpatterns = [
        path('books/<yyyy:year>/', views.book_year),
    ]
    

    以上这些就是2.0以上版本的新的url匹配规则了, 下面的例子仍然以1.0版本的url(正则)来做路由匹配.

    路由配置中的斜杠问题

    我们自己写的路由函数中通常会在最后添加上/后缀, 然后在settings全局配置中添加上下面一句话

    APPEND_SLASH = True
    

    这个配置项为true的含义是当用户输入的url不匹配任何一个url路由的时候, Django会先判断这个url是否是以/结尾的,如果是, 则直接返回404错误, 如果不以/结尾, 会自动加上/生成一个新的url, 然后再去urlConf进行匹配, 如果能访问的话, 便会返回一个301状态码, 并把这个新的url传给浏览器 , 如果还是不能访问, 则还会报404错误.

    通常为了方便我们都会默认添加上这个斜杠.

    分组

    无名分组

    下面的示例使用简单的正则表达式分组匹配(通过圆括号)来捕获URL中的值并以位置参数形式传递给视图。

    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'books/(d+)', views.books),
    ]
    # views.py
    def books(request, args):
        # 在无名分组中, 括号捕获到的参数都会以位置参数的形式传递到视图函数中, 名字可以随意取
        print(args)
        return HttpResponse(f'无名分组, 接收到参数{args}')
    

    有名分组

    在更高级的用法中,可以使用分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图.

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

    urlpatterns = [
        url(r'books2/(?P<book_id>d+)', views.books2),
    ]
    def books2(request, book_id):
        return HttpResponse(f'收到有名分组{book_id}')
    

    有名分组能使表达式更加清晰, 并且捕获到的参数会以关键字参数的形式传递到视图函数中, 这意味着参数的顺序不那么重要, 也不容易出错.

    路由分发

    当应用有很多的时候, 且每一个应用都有上百个视图函数的时候, 这时如果都写在总路由里面, 那么显然不合理, 和拆分的模块的思路一样, 路由可以写在多个文件中, 然后使用include函数导入即可.

    urls = [
        url(r'^test1/', views.test1),
    ]
    urlpatterns = [
        url(r'test/', include(urls)),
    ]
    
    def test1(request):
        return HttpResponse('路由分发的test1')
    

    这时候再浏览器里敲地址test/开头的都会被分发到urls这个子路由里面, 进行匹配, 和之前的逻辑并无不同.

    命名URL和反向解析

    在使用Django 项目时,一个常见的需求是获得URL的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。

    人们强烈希望不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 毫不相关的专门的URL 生成机制,因为这样容易导致一定程度上产生过期的URL。

    换句话讲,需要的是一个DRY 机制。除了其它有点,它还允许设计的URL 可以自动更新而不用遍历项目的源代码来搜索并替换过期的URL。

    获取一个URL 最开始想到的信息是处理它视图的标识(例如名字),查找正确的URL 的其它必要的信息有视图参数的类型(位置参数、关键字参数)和值。
    Django 提供一个办法是让URL 映射是URL 设计唯一的地方。你填充你的URLconf,然后可以双向使用它:

    • 根据用户/浏览器发起的URL 请求,它调用正确的Django 视图,并从URL 中提取它的参数需要的值。
    • 根据Django 视图的标识和将要传递给它的参数的值,获取与之关联的URL。

    第一种方式是我们在前面的章节中一直讨论的用法。第二种方式叫做反向解析URL、反向URL 匹配、反向URL 查询或者简单的URL 反查。
    在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:

    • 在模板中:使用url模板标签。
    • 在Python 代码中:使用django.core.urlresolvers.reverse() 函数。

    以上是官方文档的直白翻译, 用大白话讲就是我们不希望在一些需要使用到url的地方把这个url的地址写死, 后期我们可能由于升级等原因需要改动url地址, 那么原来写死的url就可能过期无效, 这时候维护更新就非常麻烦, 所以需要找到一种反映映射关系的技术. 而这种方式就是Django给我们提供的url命名与url反向解析.

    在上面我们也提到了url的基本配置还可以填写一个name参数, 这个就是当前url的名字. 使用方式如下所示:

    urlpatterns = [
        url(r'books/(?P<book_id>d+)', views.books, name='book_detail'),
    ]
    
    from django.urls import reverse
    
    def books(request, book_id):
        print(reverse('book_detail', kwargs={'book_id': 5}))  # out: /books/5
        return HttpResponse('Book')
    # 模板中的写法
    {% url 'book_detail' book_id=6 %}
    

    正确的反向解析需要正确的传递给路由需要的参数, 如果是无名分组, 需要传递给args参数, 如果是有名分组, 需要将参数传递给kwargs字典中.

    名称空间

    真实的项目中会存在多个app, 而这些app又分别由许多人共同开发, 这就涉及到可能多个url名称会相同, 这时候想要正确的反向解析到对应app里的视图函数, 就需要利用到名称空间了.

    具体用法见下面:

    # 总路由, 进行分发, 配置各自的名称空间
    urlpatterns = [
        url(r'app01/', include('app01.urls', namespace='app01')),
        url(r'app02/', include('app02.urls', namespace='app02')),
    ]
    
    # app01的路由和视图
    urlpatterns = [
        url(r'books/(?P<book_id>d+)', views.books, name='book_detail'),
        url(r'test/', views.test),
    ]
    # 视图
    def books(request, book_id):
        print(reverse('book_detail', kwargs={'book_id': 5}))
        return HttpResponse('Book')
    
    def test(request):
        print(reverse('app01:book_detail', kwargs={'book_id': 5}))
        print(reverse('app02:book_detail', kwargs={'book_id': 7}))
        return HttpResponse('ok')
    
    # app02的路由和视图
    urlpatterns = [
        url(r'books/(?P<book_id>d+)', views.books, name='book_detail'),
    ]
    
    # 现在在浏览器敲入http://127.0.0.1:8000/app01/test/进行测试, 最后test函数可以正确的输出
    # 结果也如预期正确解析出来
    # /app01/books/5
    # /app02/books/7
    

    模板中的使用和反向解析一样, 只需要加上名称空间名字: url名字即可.

    {% url 'app01:book_detail' book_id=7 %}
    
  • 相关阅读:
    VTIL & NoVmp 源码简要分析
    无需 Root 使用 IDA 调试 Android Native
    QWB 2021 StandOnTheGaints Writeup
    外部调用可执行文件中的函数
    CISCN 2021 西南赛区 Fix Writeup
    CISCN 2021 西南赛区 Reverse Writeup
    AES Block Cipher Implementation in C
    MTCTF 2021 Inject Writeup
    【题解】电子科技大学第十九届 ACM 程序设计竞赛
    洛谷P2596 [ZJOI2006]书架
  • 原文地址:https://www.cnblogs.com/yscl/p/11576248.html
Copyright © 2011-2022 走看看