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 = [

]

然后再主路由中,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')),  # 私信留言模块
]

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

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

路由级联包含

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

from django.urls import include, path
from . import views

urlpatterns = [
	path('<page_slug>-<page_id>/',  # 外层url访问路径
		include([
			path('history/', views.history),  # 内层url访问路径
			path('edit/', views.edit),
		])
	)
]

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

__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'),           # 用户登录
         ]))
]

路由正则匹配

通过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<year>\d{4})/$', views.article_year, name='article_year'),
    re_path('^articles_month/(?P<year>\d{4})/(?P<month>)\d{2}/$', views.article_month, name='article_month'),
    re_path('^article_detail/(?P<article_id>\S{10,})$/',views.article_detail, name='article_detail'),
]

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

下面重构下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

路由位置参数

上面使用正则路由,还有一种路由方式,将请求参数通过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/<int:year>/', views.article_year, name='article_year'),
    re_path('^articles/<int:year>/<int:month>/', views.article_month, name='article_month'),
    re_path('^articles/<int:article_id>/', views.article_detail, name='article_detail'),
]

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

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

自定义路由路径转换器

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

下面来定义一个自己的路由路径转换器。在项目的根管理项目中添加一个路由路径转换模块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

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

  • 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 = [
	......
]

然后在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/<yyyy:year>/', views.article_year, name='article_year'),
    ......
]

路由指定错误页面

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

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

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

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

DEBUG = False
ALLOWED_HOSTS = ['*', ]

一般会在根目录下创建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',
            ],
        },
    },
]

接下来就在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>

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

django-admin startapp common

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

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': '请稍等,店小二正在路上维修呢'})

然后重构主路由,打开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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ethan-running

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值