• Django(6):详解Django路由设计


    上一章:Django增删改查操作

    视图模板概述

    Django是基于MVT处理模式的WEB框架。后面会讲解V,也就是View(视图)的部分。

    Django的模板有两个核心部分:视图(View)和模板(Template)。视图主要包括路由映射、内置视图处理组件、视图处理函数等;模板主要包含变量数据的展示、数据展示流程控制、数据号暂时格式化过滤转换等。

    下图Django里MVT数据转换流程图:

    在这里插入图片描述

    路由请求路程

    Django中的路由管理参考了路由器的处理过程:由主模块路由管理并分配不同模块的请求路径给子路由,子路由模块将不同业务的请求路由到不同的视图处理组件。

    Django中的用户请求流程,是通过框架内置的处理算法进行匹配映射,具体流程如下:

    1. 启动项目,Django会加载配置文件settings.py中的路由配置选项ROOT_URLCONF来定位主路由模块。
    2. 接受用户传入的请求对象HttpRequest,在请求对象中拆分URL访问路径,在主路由模块中查找变量urlpatterns,该变量的值是列表数据,包含了所有定义的路由映射对象。路由对象时django.urls.poath()或者django.urls.re_path()
    3. Django按照主路由、子路由的匹配方式进行路径的匹配,并且在匹配到第一个成功的映射关系后,调用执行该映射关系指定的视图处理组件。
    4. 如果所有的路径匹配都失败或出现异常,Django会根据匹配结果调用适当的错误处理视图。

    路由对象

    在Django中核心组件主要有两个:

    • django.urls.path:常规路径的路由处理组件。
    • django.urls.re_path:附带正则匹配的路由处理组件。

    以前几章讲过的博客项目为例,我们通过根管理项目来管理用户模块、文章模块等。下面对项目的路由进行重构。

    首先需要在各个子项目中创建子里有模块urls.py,在其中添加如下代码:

    from django.urls import path
    from . import views
    
    # 定义路由映射组件
    urlpatterns = [
    
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    然后再主路由中,personal_blog/personal_blog/urls.py,添加如下代码:

    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include('author.urls')),  # 用户模块
        path('', include('article.urls')),  # 文章模块
        path('', include('comment.urls')),  # 评论
        path('', include('album.urls')),  # 相册模块
        path('', include('message.urls')),  # 私信留言模块
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    前面的章节,已经把用户模块的登录和注册功能开发完毕,现在重构子路由如下:

    __author__ = 'Ethan'
    __version__ = 'v1.0.0'
    
    from django.urls import path
    from . import views
    
    # 路由模块名称
    app_name = 'author'
    
    # 添加路由配置
    urlpatterns = [
        path('author/register/', views.author_register, name='register'),
        path('author/login/', views.author_login, name='login'),
    ]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    路由级联包含

    像上面那样,发现访问路径出现了路由冗余。因此就需要用到路由嵌套功能,如下:

    from django.urls import include, path
    from . import views
    
    urlpatterns = [
    	path('-/',  # 外层url访问路径
    		include([
    			path('history/', views.history),  # 内层url访问路径
    			path('edit/', views.edit),
    		])
    	)
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    还是以上面的博客项目的用户模块为例,重构代码如下:

    __author__ = 'Ethan'
    __version__ = 'v1.0.0'
    
    from django.urls import path, include
    from . import views
    
    # 路由模块名称
    app_name = 'author'
    
    # 添加路由配置
    urlpatterns = [
        path('author/',
             include([
                path('register/', views.author_register, name='register'),  # 用户注册
                path('login/', views.author_login, name='login'),           # 用户登录
             ]))
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    路由正则匹配

    通过django.urls.re_path()实例,用于对正则表达式提供支持。用法与前面的path实例一致,只是在具有正则的路径,要使用re_path。

    以博客项目的文章模块为例,增加三个接口:查询所有文章、查询指定年份的文章、查询指定编号的文章。编辑article/urls.py如下:

    from django.urls import path, re_path
    from . import views
    
    # 路由模块名称
    app_name = 'article'
    
    # 添加路由配置
    urlpatterns = [
        re_path('^article/(?P\d{4})/$', views.article_year, name='article_year'),
        re_path('^articles_month/(?P\d{4})/(?P)\d{2}/$', views.article_month, name='article_month'),
        re_path('^article_detail/(?P\S{10,})$/',views.article_detail, name='article_detail'),
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    具体的正则表达式这里不再说明了。

    下面重构下article/views.py,完成上面路由关联的视图函数:

    from django.shortcuts import render
    
    
    def article_year(request, year):
        """查询指定年份的文章,year变量会接受路由中传递的数据"""
        print("查询指定年份的文章:%s年" % year)
        pass
    
    
    def article_month(request, year, month):
        """查询指定月份的文章,year和month变量会接受路由中传递的数据"""
        print("查询指定年份的文章:%s年%s月" % (year, month))
        pass
        
        
    def article_detail(request, article_id):
        """查询指定编号的文章,article_id变量会接受路由中传递的数据"""
        print("查询指定编号的文章,编号:%s" % article_id)
        pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    路由位置参数

    上面使用正则路由,还有一种路由方式,将请求参数通过URL地址传递给视图处理函数进行处理。重构article/urls.py代码如下:

    from django.urls import path, re_path
    from . import views
    
    # 路由模块名称
    app_name = 'article'
    
    # 添加路由配置
    urlpatterns = [
        path('article_list/', views.article_list, name='article_list'),
        re_path('^articles//', views.article_year, name='article_year'),
        re_path('^articles///', views.article_month, name='article_month'),
        re_path('^articles//', views.article_detail, name='article_detail'),
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    路由参数类型:也称路由路径转换器,在Django框架中主要有如下封装的路由路径转换器:

    • str:可以匹配除路径分隔符之外的任何非空字符。
    • int:可以匹配0或任意正整数。
    • slug:可以匹配任意一个由ASCII字母或数字组成的字符串。
    • uuid:可以匹配格式化的UUID,防止多个URL地址被映射到同一页面。
    • path:可以匹配任意非空字符串,包括路径分隔符“/”,功能比str强大,可以匹配完整的URL路径。

    自定义路由路径转换器

    上面的路由路径转换器已经可以满足我们大部分需求了。但是有时候想自定义一个路由转换器,比如,上面路由中的,并不能精确匹配年份,因为他只要接受一个正整数就完成来了路由的调用,会匹配一些无效的年份数据。

    下面来定义一个自己的路由路径转换器。在项目的根管理项目中添加一个路由路径转换模块route_converter.py

    
    class RouterYearConverter:
        """自定义年份类型转换器"""
        regex = '[0-9]{4}'
        
        def to_python(self, value):
            return int(value)
        
        def to_url(self, value):
            return '%04d' % value
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在自定义路由转换器中,主要包含一个属性和两个函数:

    • regex属性是一个用字符串表示的正则表达式
    • to_python用于将接收到的数据转换并传递给绑定的视图函数处理,如果数据转换失败,爬出ValueError错误
    • to_url用于将python中对应类型的数据转成字符串,用作URL。

    上面定义好路由路径转换器后,还需要注册到路由组件。打开persnal_blog/persnal_blog/urls.py,添加注册代码如下:

    """personal_blog URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/4.1/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    from django.contrib import admin
    from django.urls import path, include, register_converter
    from route_converter import RouterYearConverter
    
    # 注册年份类型转换器
    register_converter(RouterYearConverter, 'yyyy')
    
    urlpatterns = [
    	......
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    然后在article/urls.py中使用转换器,代码如下:

    from django.urls import path, re_path
    from . import views
    
    # 路由模块名称
    app_name = 'article'
    
    # 添加路由配置
    urlpatterns = [
        path('article_list/', views.article_list, name='article_list'),
        re_path('^articles//', views.article_year, name='article_year'),
        ......
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    路由指定错误页面

    在web请求中,会出现非正常请求,比如404、403、500等,Django的路由模块也对场景的错误状态进行了封装。对于通用的错误状态,默认配置如下:

    # 对于常见的不同请求状态的默认处理,并返回内置的默认错误页面
    handler400 = defaults.bad_request
    handler403 = defaults.permission_denied
    handler404 = defaults.page_not_found
    handler500 = defaults.server_error
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果想自定义错误页面,则需要早路由中指定错误配置处理器,绑定相应的视图处理函数完成错误页面的自定义处理。

    首先需要关闭调试模式,因为在调试模式下自定义的错误不会生效。打开项目配置文件,修改如下:

    DEBUG = False
    ALLOWED_HOSTS = ['*', ]
    
    • 1
    • 2

    一般会在根目录下创建html_file文件夹,用于存放静态文件,并在配置文件中添加文件夹的路径。修改配置文件如下:

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'html_file'), ],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    接下来就在html_file文件夹中创建错误页面pageerror.html,如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>page error</title>
    </head>
    <body>
    <h1>{{status_code}}, {{message}}</h1>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    通常在一个项目中会有一些公共的功能,因此创建一个公共子模块common,用于处理公共功能和多个模块之间的交互功能。

    django-admin startapp common
    
    • 1

    在其视图模块中添加错误页面处理函数,代码如下:

    from django.shortcuts import render
    
    
    def page400error(request, exception,  **kwargs):
        return render(request, 'pageerror.html',
                      {'status_code': '400', 'message': '客官,您的请求出问题了'})
    
    
    def page403error(request, exception, **kwargs):
        return render(request, 'pageerror.html',
                      {'status_code': '403', 'message': '客官,您的权限不够呢'})
    
    
    def page404error(request, exception, **kwargs):
        return render(request, 'pageerror.html',
                      {'status_code': '404', 'message': '抱歉客官,您要访问的资源还没有上线呢'})
    
    
    def page500error(request, **kwargs):
        return render(request, 'pageerror.html',
                      {'status_code': '500', 'message': '请稍等,店小二正在路上维修呢'})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    然后重构主路由,打开personal_blog/personal_blog/urls.py,定义视图处理器,代码如下:

    from django.contrib import admin
    from django.urls import path, include, register_converter
    from route_converter import RouterYearConverter
    
    # 注册年份类型转换器
    register_converter(RouterYearConverter, 'yyyy')
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include('author.urls')),  # 用户模块
        path('', include('article.urls')),  # 文章模块
        path('', include('comment.urls')),  # 评论
        path('', include('album.urls')),  # 相册模块
        path('', include('message.urls')),  # 私信留言模块
        path('', include('common.urls')),   # 公共模块
    ]
    
    # 定制错误页面
    handler400 = 'common.views.page400error'
    handler403 = 'common.views.page403error'
    handler404 = 'common.views.page404error'
    handler500 = 'common.views.page500error'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    当访问不存在的页面,就会显示如下:
    在这里插入图片描述

  • 相关阅读:
    ABAP MD04增强排除MRP元素
    Godot引擎小白入门指南
    误删docker容器(container)后的恢复
    软件工程导论——第三章——需求分析
    如何使用JavaMailSender给曾经心爱的她发送一封特别的邮件
    BI行业分析思维框架 - 环保行业分析(三)三层经营分析框架
    详解module.exports与exports,export与export default,import 与require
    手机ip地址是实时位置吗
    OKhttp上传图片视频
    py2neo代码封装
  • 原文地址:https://blog.csdn.net/qq_43745578/article/details/127593942