上一章:Django增删改查操作
视图模板概述
Django是基于MVT处理模式的WEB框架。后面会讲解V,也就是View(视图)的部分。
Django的模板有两个核心部分:视图(View)和模板(Template)。视图主要包括路由映射、内置视图处理组件、视图处理函数等;模板主要包含变量数据的展示、数据展示流程控制、数据号暂时格式化过滤转换等。
下图Django里MVT数据转换流程图:
路由请求路程
Django中的路由管理参考了路由器的处理过程:由主模块路由管理并分配不同模块的请求路径给子路由,子路由模块将不同业务的请求路由到不同的视图处理组件。
Django中的用户请求流程,是通过框架内置的处理算法进行匹配映射,具体流程如下:
- 启动项目,Django会加载配置文件settings.py中的路由配置选项ROOT_URLCONF来定位主路由模块。
- 接受用户传入的请求对象
HttpRequest
,在请求对象中拆分URL访问路径,在主路由模块中查找变量urlpatterns,该变量的值是列表数据,包含了所有定义的路由映射对象。路由对象时django.urls.poath()
或者django.urls.re_path()
。 - Django按照主路由、子路由的匹配方式进行路径的匹配,并且在匹配到第一个成功的映射关系后,调用执行该映射关系指定的视图处理组件。
- 如果所有的路径匹配都失败或出现异常,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'
当访问不存在的页面,就会显示如下: