编写视图

一个视图函数(或简称为视图)是一个 Python 函数,它接受 Web 请求并返回一个 Web 响应。这个响应可以是 Web 页面的 HTML 内容,或者重定向,或者404错误,或者 XML 文档,或一个图片…或是任何内容。视图本身包含返回响应所需的任何逻辑。这个代码可以存在任何地方,只要它在你的 Python 路径上就行。可以说,不需要其他东西,这里并没有魔法。为了将代码放置在某处,约定将视图放在名为 views.py 的文件里,这个文件放置在项目或应用目录里。

一个简单的视图

这里是一个以 HTML 文档形式返回当前日期和时间的视图:

  1. from django.http import HttpResponse
  2. import datetime
  3. def current_datetime(request):
  4. now = datetime.datetime.now()
  5. html = "<html><body>It is now %s.</body></html>" % now
  6. return HttpResponse(html)

让我们来完成这个代码:

  • 首先,我们从 django.http 模块导入类 HttpResponse ,以及 Python 的 datetime 库。

  • 然后,我们定义一个名为 current_datetime 的函数。这是一个视图函数。每个视图函数都将 HttpRequest 对象作为第一个参数,通常名为 request

    注意视图函数名称无关紧要;它不需要以特定的名称来让 Django 识别它。我们在这里命名 current_datetime ,因为这个名字可以清楚的表示它的用途。

  • 视图返回一个包含生成的响应的 HttpResponse 对象。每个视图函数都要返回 HttpResponse 对象。(有例外,我们稍后再讲)

Django 时区

Django 包含 TIME_ZONE 设置,默认是 America/Chicago 。你可以在配置文件里改成你所在的时区。

将 URL 映射到视图

因此,回顾一下,这个视图函数返回包含当前日期时间的HTML页面。如果在特定的 URL 使用这个视图,你需要创建 URLconf ;查看 URL调度器 的操作说明。

返回错误信息

Django 提供了有关返回 HTTP 错误代码的帮助。HttpResponse 的子类除了200外,还有很多常见的 HTTP 状态代码。你可以在 request/response 文档中找到所有可用子类的列表。返回这些子类中某个子类的实例而不是 HttpResponse 来表示错误。比如:

  1. from django.http import HttpResponse, HttpResponseNotFound
  2. def my_view(request):
  3. # ...
  4. if foo:
  5. return HttpResponseNotFound('<h1>Page not found</h1>')
  6. else:
  7. return HttpResponse('<h1>Page was found</h1>')

并不是每个可用 HTTP 响应代码都有专门指定的子类,因为它们很多并不常见。然而,如 HttpResponse 文档中所述的那样,你也可以将 HTTP 状态代码传递给 HttpResponse 的构造函数,这样就可以为任何状态代码创建返回类。比如:

  1. from django.http import HttpResponse
  2. def my_view(request):
  3. # ...
  4. # Return a "created" (201) response code.
  5. return HttpResponse(status=201)

因为 404 错误是最常见的 HTTP 错误,这里有更简单的方法来处理这些错误。

Http404 异常

class django.http.``Http404

当你返回错误,例如 HttpResponseNotFound ,你需要定义错误页面的 HTML 。

  1. return HttpResponseNotFound('<h1>Page not found</h1>')

为方便起见,在你的网站里有个一致的 404 错误页面是个好办法,Django 提供 Http404 异常。如果你在视图的任何地方引发了 Http404 ,Django 会捕捉到它并且返回标准的错误页面,连同 HTTP 错误代码 404 。

用法示例:

  1. from django.http import Http404
  2. from django.shortcuts import render
  3. from polls.models import Poll
  4. def detail(request, poll_id):
  5. try:
  6. p = Poll.objects.get(pk=poll_id)
  7. except Poll.DoesNotExist:
  8. raise Http404("Poll does not exist")
  9. return render(request, 'polls/detail.html', {'poll': p})

为了在 Django 返回404时显示自定义的 HTML,你可以创建名为 404.html 的HTML模板,并将其放置在你的模板树顶层。这个模板将在 DEBUG 设为 False 时提供。

DEBUGTrue 时,你可以提供 Http404 信息,并且在标准的 404 调试模板里显示。使用这些信息来调试;它们通常不适合在生产环境下的404模板。

自定义报错视图

Django 里默认的报错视图应该能满足大部分的 Web 应用,但你也可以很方便的自定义。指定处理程序,如下方所示。(在其他地方配置它不会有任何效果)。

可以用 handler404: 覆盖 page_not_found() 视图:

  1. handler404 = 'mysite.views.my_custom_page_not_found_view'

可以用 handler500: 覆盖 server_error() 视图:

  1. handler500 = 'mysite.views.my_custom_error_view'

可以用 handler403: 覆盖 permission_denied() 视图:

  1. handler403 = 'mysite.views.my_custom_permission_denied_view'

可以用 handler400: 覆盖 bad_request() 视图:

  1. handler400 = 'mysite.views.my_custom_bad_request_view'

参见

使用 CSRF_FAILURE_VIEW 来覆盖 CSRF 报错视图。

测试自定义报错视图

为了测试自定义报错处理的响应,可以适当地在测试视图里引发异常。例如:

  1. from django.core.exceptions import PermissionDenied
  2. from django.http import HttpResponse
  3. from django.test import SimpleTestCase, override_settings
  4. from django.urls import path
  5. def response_error_handler(request, exception=None):
  6. return HttpResponse('Error handler content', status=403)
  7. def permission_denied_view(request):
  8. raise PermissionDenied
  9. urlpatterns = [
  10. path('403/', permission_denied_view),
  11. ]
  12. handler403 = response_error_handler
  13. # ROOT_URLCONF must specify the module that contains handler403 = ...
  14. @override_settings(ROOT_URLCONF=__name__)
  15. class CustomErrorHandlerTests(SimpleTestCase):
  16. def test_handler_renders_template_response(self):
  17. response = self.client.get('/403/')
  18. # Make assertions on the response here. For example:
  19. self.assertContains(response, 'Error handler content', status_code=403)

Async views

New in Django 3.1.

As well as being synchronous functions, views can also be asynchronous (“async”) functions, normally defined using Python’s async def syntax. Django will automatically detect these and run them in an async context. However, you will need to use an async server based on ASGI to get their performance benefits.

Here’s an example of an async view:

  1. import datetime
  2. from django.http import HttpResponse
  3. async def current_datetime(request):
  4. now = datetime.datetime.now()
  5. html = '<html><body>It is now %s.</body></html>' % now
  6. return HttpResponse(html)

You can read more about Django’s async support, and how to best use async views, in 异步支持.