本节主要介绍Django框架视图层中视图函数方面的内容,包括视图函数的基本概念、简单视图函数、映射URL至视图和异步视图等。视图函数是基于Django框架进行视图层开发的基础。
所谓视图函数(简称视图),本质上就是一个Python函数,用于接收Web请求并且返回Web响应。
Web响应可以包含很多种类的内容,比较常见的有HTML网页、重定向和404错误,也可以是XML文档和图像文件等。另外,无论视图函数的具体处理逻辑如何定义,建议都返回某种类型的Web响应。
图4.2 ViewDjango项目应用结构
对于视图函数的代码而言,可以写在项目的任何Python目录下面。但是,对于基于Django框架的Web项目而言,通常约定将视图函数写在项目或应用目录中名称为“views.py”的文件中。
在本小节中,我们设计一个基于Django框架的Web项目应用,实现将当前日期和时间编码为HTML文档并返回的简单视图函数。
首先,将该Web项目应用的名称定义为“ViewDjango”,实现返回当前日期和时间的简单视图函数应用的名称定义为“SimpleView”,具体文件结构如图4.2所示。
在图4.2中,ViewDjango为项目根目录,SimpleView为具体的应用目录。
然后,重新定义ViewDjango项目根目录下的路由文件urls.py,实现到SimpleView应用的路由路径,具体代码如下:
【代码4-22】(ViewDjango\ViewDjango\urls.py文件)
【代码分析】
在第07~10行代码中,定义了ViewDjango项目应用的根URLconf模块。其中,第08行代码中,通过path()函数定义了一个路由路径'simple/',该路径对应通过include方式包括的SimpleView应用的URLconf模块'SimpleView.urls'。
接下来,定义SimpleDjango应用目录中的路由文件urls.py,具体代码如下:
【代码4-23】(ViewDjango\SimpleView\urls.py文件)
【代码分析】
在第07~10行代码中,定义了SimpleDjango应用的URLconf模块。详细说明如下:
· 第08行代码中,通过path()函数将SimpleDjango应用的默认路径("")解析为视图函数views.index。
· 第09行代码中,通过path()函数将路径"curdatetime/"解析为视图函数views.current_datetime。
最后,定义SimpleDjango应用中的视图函数文件views.py,具体代码如下:
【代码4-24】(ViewDjango\SimpleView\views.py文件)
【代码分析】
在第08、09行代码中,定义了默认视图函数views.index。其中,第09行代码通过HttpResponse()方法返回信息"Hello, SimpleView App!"。
在第12行代码中,导入了日期和时间类型对象datetime。
在第13~16行代码中,定义了日期视图函数views.current_datetime。详细说明如下:
· 在第16行代码中,通过日期和时间类型对象datetime调用now()方法,获取当前时间(now)。
· 在第17行代码中,定义了一段HTML页面代码(html),并将当前时间(now)传递到这段页面代码中。
· 在第18行代码中,通过HttpResponse()方法返回页面代码。
下面,通过FireFox浏览器测试一下SimpleView简单视图应用。首先,通过Django服务器运行ViewDjango项目应用,并在浏览器中输入SimpleView应用默认路由地址“http://localhost:8000/ simple/”,结果如图4.3所示。页面中显示了第09行代码通过HttpResponse()方法返回的信息“Hello, SimpleView!”。
然后,继续在浏览器中输入SimpleView应用简单视图的路由地址“http://localhost:8000/simple/curdatetime/”,结果如图4.4所示。页面中显示了第18行代码通过HttpResponse()方法返回的当前时间信息(now)。
图4.3 SimpleView应用默认路由
图4.4 SimpleView简单视图路由
在Django框架中返回HTTP错误代码是非常简单的。HttpResponse类的许多子类对应着一些常用的HTTP状态码,例如HttpResponseNotFound子类对应的HTTP 404错误,当然这里面不包括200状态码(表示“OK”)。Django为了标识一个错误,直接返回那些子类中的一个实例,而不是普通的HttpResponse对象。
在本小节中,我们通过HttpResponseNotFound子类设计一个返回错误视图的应用,用来模拟返回404错误状态。
首先,将返回错误视图函数应用的名称定义为“ErrorView”,具体文件结构如图4.5所示。
图4.5 ErrorView返回错误视图应用的结构
然后,重新定义ViewDjango项目根目录下的路由文件urls.py,实现到ErrorView应用的路由路径,具体代码如下:
【代码4-25】(ViewDjango\ViewDjango\urls.py文件)
【代码分析】
在第07~11行代码中,重新定义了ViewDjango项目应用的根URLconf模块。详细说明如下:
· 在第09行代码中,通过path()函数新增了一个路由路径error/',该路径对应通过include方式包括的ErrorView应用的URLconf模块'ErrorView.urls'。
接下来,定义ErrorView应用目录中的路由文件urls.py,具体代码如下:
【代码4-26】(ViewDjango\ErrorView\urls.py文件)
【代码分析】
在第07~10行代码中,定义了ErrorView应用的URLconf模块。详细说明如下:
· 在第08行代码中,通过path()函数将ErrorView应用的默认路径("")解析为视图函数views.index。
· 在第09行代码中,通过path()函数将路径“pagenotfound/<int:p>/”解析为视图函数views.error_view。其中,添加了一个路由参数<int:p>,参数值p用于选择不同的视图返回值。
最后,定义ErrorView应用中的视图函数文件views.py,具体代码如下:
【代码4-27】(ViewDjango\ErrorView\views.py文件)
【代码分析】
第09、10行代码中,定义了默认视图函数views.index。
第13~18行代码中,定义了错误视图函数views.error_view。其中,第15~18行代码通过if条件语句判断参数p的布尔值,选择是使用HttpResponse()方法,还是HttpResponseNotFound()方法返回信息。
下面,通过FireFox浏览器测试一下ErrorView返回错误视图应用。首先,通过Django服务器运行ViewDjango项目应用,并在浏览器中输入ErrorView应用默认路由地址“http://localhost:8000/error/”,结果如图4.6所示。
图4.6 ErrorView应用默认路由
然后,在浏览器中输入ErrorView应用返回错误视图的路由地址“error/pagenotfound/1/”(注意,增加了整型路由参数1),结果如图4.7所示。打开路由地址“error/pagenotfound/1/”后,页面中显示了第16行代码通过HttpResponse()方法返回的信息。
图4.7 ErrorView返回错误视图路由(1)
再通过FireFox浏览器控制台查看一下返回的HTTP状态码,结果如图4.8所示。HTTP状态码显示为“Status:200 OK”。
图4.8 ErrorView返回错误视图路由(2)
继续在浏览器中输入ErrorView应用返回错误视图的路由地址“error/pagenotfound/0/”(注意,整型路由参数更改为0),结果如图4.9所示。打开路由地址“error/pagenotfound/0/”后,页面中显示了第18行代码通过HttpResponseNotFound()方法返回的信息。
图4.9 ErrorView返回错误视图路由(3)
再通过FireFox浏览器控制台查看一下返回的HTTP状态码,结果如图4.10所示。HTTP状态码显示为“Status:404 Not Found”,这说明HttpResponseNotFound子类直接返回了HTTP 404错误。
图4.10 ErrorView返回错误视图路由(4)
在Django框架中,还支持直接返回HTTP状态码的操作。可以通过向HttpResponse子类的构造器传递HTTP状态码来创建任何想要的HTTP状态码的返回类。
在本小节中,我们设计一个直接返回HTTP状态码视图的应用,用来模拟返回任意HTTP状态码。
首先,将直接返回状态码视图函数应用的名称定义为StatusView,文件结构如图4.11所示。
图4.11 StatusView直接返回状态码视图应用的结构
然后,重新定义ViewDjango项目根目录下的路由文件urls.py,实现到StatusView应用的路由路径,具体代码如下:
【代码4-28】(ViewDjango\ViewDjango\urls.py文件)
【代码分析】
在第07~12行代码中,重新定义了ViewDjango项目应用的根URLconf模块。详细说明如下:
· 在第10行代码中,通过path()函数新增了一个路由路径status/,该路径对应通过include方式包括的StatusView应用的URLconf模块StatusView.urls。
接下来,定义StatusView应用目录中的路由文件urls.py,具体代码如下:
【代码4-29】(ViewDjango\StatusView\urls.py文件)
【代码分析】
在第07~10行代码中,定义了StatusView应用的URLconf模块。详细说明如下:
· 在第08行代码中,通过path()函数将StatusView应用的默认路径“""”解析为视图函数views.index。
· 在第09行代码中,通过path()函数将路径"statuscode/<int:scode>/"解析为视图函数views.status_code_view。其中,添加了一个路由参数<int:scode>,参数值scode用于选择不同的视图返回值。
最后,定义StatusView应用中的视图函数文件views.py,具体代码如下:
【代码4-30】(ViewDjango\StatusView\views.py文件)
【代码分析】
在第09、10行代码中,定义了默认视图函数views.index。
在第13~16行代码中,定义了直接返回状态码的视图函数views.status_code_view。其中,第16行代码通过在HttpResponse()方法中添加参数status=scode,直接将通过URL传递过来的HTTP状态码scode返回。
下面,通过FireFox浏览器测试一下StatusView直接返回状态码视图应用。首先,通过Django服务器运行ViewDjango项目应用,并在浏览器中输入StatusView应用默认路由地址“http://localhost:8000/status/”,结果如图4.12所示。
图4.12 StatusView应用默认路由
然后,在浏览器中输入StatusView应用直接返回状态码视图的路由地址“status/statuscode/201/”,(注意,增加了整型路由参数201),如图4.13所示。
图4.13 StatusView直接返回状态码视图路由(1)
打开路由地址“status/statuscode/201/”后,再通过FireFox浏览器控制台查看一下返回的HTTP状态码,结果如图4.14所示。HTTP状态码显示为“Status:201 Created”,状态码201与URL地址中输入的数值是对应的。
图4.14 StatusView直接返回状态码视图路由(2)
如果觉得状态码201比较常见,不具备一定的代表性,那么我们尝试在浏览器中输入路由地址“status/statuscode/250/”(注意,这里的整型路由参数250不是常见的HTTP状态码),结果如图4.15所示。
图4.15 StatusView直接返回状态码视图路由(3)
打开路由地址“status/statuscode/250/”后,再通过FireFox浏览器控制台查看一下返回的HTTP状态码,结果如图4.16所示。HTTP状态码显示为“Status:250 Unknown Status Code”,状态码250与URL地址中输入的数值是对应的,并且还提示了该状态码为未知(无预定义)的HTTP状态码。
图4.16 StatusView直接返回状态码视图路由(4)
在Django框架中,当返回错误(如:HttpResponseNotFound)时,一般需要定义错误页面的HTML模板。为了方便设计人员开发,Django框架内置了Http404异常(仅定义了该异常,没有类似Http400、Http403等的异常)。
假如Django代码在视图的任意一个地方引发了Http404异常,那么Django框架就会捕捉到该异常,并且返回标准的错误页面(连同HTTP错误状态代码404)。不过要记住,只有在Django项目设置中将DEBUG参数设置为FALSE才能实现上述功能,DEBUG参数默认是设置为TRUE的。
在本小节中,我们设计一个Http404异常视图的应用,用来实现HTTP 404错误的异常捕捉。
图4.17 Http404View异常视图应用的结构
首先,将Http404异常视图应用的名称定义为Http404View,具体文件结构如图4.17所示。然后,重新定义ViewDjango项目根目录下的路由文件urls.py,实现到Http404View应用的路由路径,具体代码如下:
【代码4-31】(ViewDjango\ViewDjango\urls.py文件)
【代码分析】
在第07~13行代码中,重新定义了ViewDjango项目应用的根URLconf模块。详细说明如下:
· 在第11行代码中,通过path()函数新增了一个路由路径“http404/”,该路径对应通过include方式包括的Http404View应用的URLconf模块Http404View.urls。
接下来,定义Http404View应用目录中的路由文件urls.py,具体代码如下:
【代码4-32】(ViewDjango\Http404View\urls.py文件)
【代码分析】
在第07~10行代码中,定义了Http404View应用的URLconf模块。详细说明如下:
· 在第08行代码中,通过path()函数将Http404View应用的默认路径("")解析为视图函数views.index。
· 在第09行代码中,通过path()函数将路径“zeroexp/”解析为视图函数views.zero_exp。
最后,定义Http404View应用中视图函数文件,具体代码如下:
【代码4-33】(ViewDjango\Http404View\views.py文件)
【代码分析】
在第04行代码中,导入了Http404模块。
在第10、11行代码中,定义了默认的视图函数views.index。
在第14~19行代码中,定义了直接返回状态码的视图函数views.zero_exp。详细说明如下:
· 在第15~18行代码中,通过try…except…语句块尝试捕获异常。其中,第16行代码定义了一个“除0”算术异常,第18行代码通过raise语句(注意不是return语句)触发Http404异常。
· 在第19行代码中,通过render()方法将参数r导向了HTML模板(arithm.html)页面。
下面,通过FireFox浏览器测试一下Http404异常视图应用。首先,通过Django服务器运行ViewDjango项目应用,并在浏览器中输入Http404View应用默认路由地址“http://localhost:8000/http404/”,结果如图4.18所示。
图4.18 Http404View应用默认路由
然后,在浏览器中输入Http404View应用异常视图的路由地址“http404/zeroexp/”,结果如图4.19所示。页面中显示出了第18行代码中通过Http404()方法定义的异常信息。
图4.19 Http404View异常视图路由(1)
不过,图4.19中显示的是调试信息,主要在开发阶段中使用。如果想显示标准的404错误页面,需要在Django项目设置中将DEBUG参数设置为FALSE,运行效果如图4.20所示。
实际上,当通过raise语句触发了Http404异常后,会执行下面的流程:
(1)Django框架会读取django.conf.urls.handler404的值,默认为django.views.defaults.page_not_found()视图。
(2)执行page_not_found()视图。
图4.20 Http404View异常视图路由(2)
(3)判断是否自定义了404.html,如果有则输出该HTML文件。
(4)如果没有,则输出默认的404提示信息。
上面的流程就给设计人员留下了两个可以自定义404页面的钩子:
· 第1个是在urls中重新指定handler404的值,也就是用哪个视图来处理404页面。
· 第2个是在page_not_found()视图中使用自定义的404.html。
上面的两种方式,一个是自定义处理视图,另一个是自定义展示404页面。自定义的404.html页面应当位于模板引擎可以搜索到的路径中。
在Django框架中,当找不到与请求匹配的URL路由地址时,或者当抛出一个异常时,将会调用一个错误处理视图。Django框架默认自带的错误视图包括400、403、404和500,分别表示请求错误、拒绝服务、页面不存在和服务器错误。
这几个错误视图分别位于Django框架的如下位置:
· handler400:django.conf.urls.handler400。
· handler403:django.conf.urls.handler403。
· handler404:django.conf.urls.handler404。
· handler500:django.conf.urls.handler500。
这几个错误视图又分别对应下面的内置视图:
· handler400:django.views.defaults.bad_request()。
· handler403:django.views.defaults.permission_denied()。
· handler404:django.views.defaults.page_not_found()。
· handler500:django.views.defaults.server_error()。
设计人员可以在根URLconf模块中设置上面这些错误视图。注意,在其他二级应用内部的URLconf模块中设置这些变量是无效的。
在Django框架中其实定义了内置的HTML模板,用于返回错误页面给用户,但是这些403、404页面不够美观,通常设计人员会根据项目需要自定义错误页面。示例如下:
首先,在根URLconf模块中额外增加一组handler对象,并依次导入views视图模块,代码如下:
【代码4-34】(项目的根urls.py文件)
【代码分析】
在第12~15行代码中,添加了一组handler对象,分别为handler400、handler403、handler404和handler500,并依次导入相对应的视图函数views.bad_request、views.permission_denied、views.page_not_found和views.error。
然后,在相应的应用视图文件app/views.py中,依次增加相对应的视图处理函数,代码如下:
【代码4-35】(app/views.py文件)
【代码分析】
在第06~08行、第10~12行、第14~16行和第18~20行代码中,分别实现了视图函数bad_request、permission_denied、page_not_found和error,并指向了各自的HTML模板文件400.html、403.html、404.html和500.html。
设计人员根据项目的实际需求,分别创建对应的HTML页面文件(400.html、403.html、404.html和500.html)即可。另外,要注意HTML模板文件的引用方式和视图文件的放置位置等。
在Django项目开发中,只有当项目配置文件的DEBUG参数设置为False时,这些错误视图才会被自动使用;而当DEBUG参数设置为True时(表示开发模式),Django框架会展示详细的错误信息页面,而不是有针对性的错误页面(这点可以参考上一小节中应用实例的测试结果)。
在Django框架最新的v3.1版本中,已经开始支持异步视图函数的开发了。编写异步视图代码,只需要用Python语言中的async def关键字语法即可,Django框架将自动探测和运行视图在同一个异步上下文环境中。
另外,为了让Django异步视图发挥其性能优势,设计人员需要启动一个基于ASGI的异步服务器。Django v3.0+版本的框架默认已经配置好了基于ASGI的异步功能。
简单的异步视图示例代码如下:
【代码4-36】(ViewDjango\AsyncView\views.py文件)
【代码分析】
在第09~12行代码中,定义了异步视图函数async_current_datetime。这个异步函数与普通函数的定义方法基本一致,关键是在通过def定义函数之前,通过async关键字来声明该函数为异步视图函数。
另外,关于异步函数还要说明以下6点:
(1)异步功能同时支持WSGI(同步)和ASGI(异步)模式。
(2)在WSGI模式下,使用异步功能会有性能损失。
(3)可以混用异步/同步视图或中间件,Django框架会自动处理其上下文。
(4)建议主要使用同步模式,在有特殊需求的场景才使用异步功能。
(5)对于Django框架的ORM系统、缓存层和其他的一些需要长时间进行网络IO调用的代码,目前依然不支持异步访问,在未来的Django版本中将会逐步实现支持。
(6)异步功能不会影响同步代码的执行速度,也不会对目前已有的项目产生明显的影响。