---
title: "编写视图"
version: 3.2
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/3.2/topics/http/views/
canonical: https://djangodocs.dev/zh-hans/3.2/topics/http/views/
---
# 编写视图

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

## 一个简单的视图

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

```
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)
```

让我们来完成这个代码：

- 首先，我们从 [`django.http`](/zh-hans/3.2/ref/request-response/#module-django.http) 模块导入类 [`HttpResponse`](/zh-hans/3.2/ref/request-response/#django.http.HttpResponse) ，以及 Python 的 `datetime` 库。
- 然后，我们定义一个名为 `current_datetime` 的函数。这是一个视图函数。每个视图函数都将 [`HttpRequest`](/zh-hans/3.2/ref/request-response/#django.http.HttpRequest) 对象作为第一个参数，通常名为 `request` 。

  注意视图函数名称无关紧要；它不需要以特定的名称来让 Django 识别它。我们在这里命名 `current_datetime` ，因为这个名字可以清楚的表示它的用途。
- 视图返回一个包含生成的响应的 [`HttpResponse`](/zh-hans/3.2/ref/request-response/#django.http.HttpResponse) 对象。每个视图函数都要返回 [`HttpResponse`](/zh-hans/3.2/ref/request-response/#django.http.HttpResponse) 对象。（有例外，我们稍后再讲）

> **Django 时区**
>
> Django 包含 [`TIME_ZONE`](/zh-hans/3.2/ref/settings/#std-setting-TIME_ZONE) 设置，默认是 `America/Chicago` 。你可以在配置文件里改成你所在的时区。

## 将 URL 映射到视图

因此，回顾一下，这个视图函数返回包含当前日期时间的HTML页面。如果在特定的 URL 使用这个视图，你需要创建 *URLconf* ；查看 [URL调度器](/zh-hans/3.2/topics/http/urls/) 的操作说明。

## 返回错误信息

Django 提供了有关返回 HTTP 错误代码的帮助。[`HttpResponse`](/zh-hans/3.2/ref/request-response/#django.http.HttpResponse) 的子类除了200外，还有很多常见的 HTTP 状态代码。你可以在 [request/response](/zh-hans/3.2/ref/request-response/#ref-httpresponse-subclasses) 文档中找到所有可用子类的列表。返回这些子类中某个子类的实例而不是 [`HttpResponse`](/zh-hans/3.2/ref/request-response/#django.http.HttpResponse) 来表示错误。比如：

```
from django.http import HttpResponse, HttpResponseNotFound

def my_view(request):
    # ...
    if foo:
        return HttpResponseNotFound('<h1>Page not found</h1>')
    else:
        return HttpResponse('<h1>Page was found</h1>')
```

并不是每个可用 HTTP 响应代码都有专门指定的子类，因为它们很多并不常见。然而，如 [`HttpResponse`](/zh-hans/3.2/ref/request-response/#django.http.HttpResponse) 文档中所述的那样，你也可以将 HTTP 状态代码传递给 [`HttpResponse`](/zh-hans/3.2/ref/request-response/#django.http.HttpResponse) 的构造函数，这样就可以为任何状态代码创建返回类。比如：

```
from django.http import HttpResponse

def my_view(request):
    # ...

    # Return a "created" (201) response code.
    return HttpResponse(status=201)
```

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

### `Http404` 异常

#### `class django.http.Http404`

当你返回错误，例如 [`HttpResponseNotFound`](/zh-hans/3.2/ref/request-response/#django.http.HttpResponseNotFound) ，你需要定义错误页面的 HTML 。

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

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

用法示例：

```
from django.http import Http404
from django.shortcuts import render
from polls.models import Poll

def detail(request, poll_id):
    try:
        p = Poll.objects.get(pk=poll_id)
    except Poll.DoesNotExist:
        raise Http404("Poll does not exist")
    return render(request, 'polls/detail.html', {'poll': p})
```

为了在 Django 返回404时显示自定义的 HTML，你可以创建名为  `404.html` 的HTML模板，并将其放置在你的模板树顶层。这个模板将在 [`DEBUG`](/zh-hans/3.2/ref/settings/#std-setting-DEBUG) 设为 `False` 时提供。

当 [`DEBUG`](/zh-hans/3.2/ref/settings/#std-setting-DEBUG) 为 `True` 时，你可以提供 `Http404` 信息，并且在标准的 404 调试模板里显示。使用这些信息来调试；它们通常不适合在生产环境下的404模板。

## 自定义报错视图

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

可以用 [`handler404`](/zh-hans/3.2/ref/urls/#django.conf.urls.handler404): 覆盖 [`page_not_found()`](/zh-hans/3.2/ref/views/#django.views.defaults.page_not_found) 视图：

```
handler404 = 'mysite.views.my_custom_page_not_found_view'
```

可以用 [`handler500`](/zh-hans/3.2/ref/urls/#django.conf.urls.handler500): 覆盖 [`server_error()`](/zh-hans/3.2/ref/views/#django.views.defaults.server_error) 视图：

```
handler500 = 'mysite.views.my_custom_error_view'
```

可以用 [`handler403`](/zh-hans/3.2/ref/urls/#django.conf.urls.handler403): 覆盖 [`permission_denied()`](/zh-hans/3.2/ref/views/#django.views.defaults.permission_denied) 视图：

```
handler403 = 'mysite.views.my_custom_permission_denied_view'
```

可以用 [`handler400`](/zh-hans/3.2/ref/urls/#django.conf.urls.handler400): 覆盖 [`bad_request()`](/zh-hans/3.2/ref/views/#django.views.defaults.bad_request) 视图：

```
handler400 = 'mysite.views.my_custom_bad_request_view'
```

> **See also**
>
> 使用 [`CSRF_FAILURE_VIEW`](/zh-hans/3.2/ref/settings/#std-setting-CSRF_FAILURE_VIEW) 来覆盖 CSRF 报错视图。

### 测试自定义报错视图

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

```
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.test import SimpleTestCase, override_settings
from django.urls import path

def response_error_handler(request, exception=None):
    return HttpResponse('Error handler content', status=403)

def permission_denied_view(request):
    raise PermissionDenied

urlpatterns = [
    path('403/', permission_denied_view),
]

handler403 = response_error_handler

# ROOT_URLCONF must specify the module that contains handler403 = ...
@override_settings(ROOT_URLCONF=__name__)
class CustomErrorHandlerTests(SimpleTestCase):

    def test_handler_renders_template_response(self):
        response = self.client.get('/403/')
        # Make assertions on the response here. For example:
        self.assertContains(response, 'Error handler content', status_code=403)
```

## 异步视图

> **New in Django 3.1**

除了同步函数，视图也可以是异步（“async”）函数，通常使用 Python 的 `async def` 语法定义。Django 会自动检测这些函数，并在异步上下文中运行它们。但是，你需要使用基于 ASGI 的异步服务器来获得它们的性能优势。

下面是一个异步视图的例子：

```
import datetime
from django.http import HttpResponse

async def current_datetime(request):
    now = datetime.datetime.now()
    html = '<html><body>It is now %s.</body></html>' % now
    return HttpResponse(html)
```

你可以在 [异步支持](/zh-hans/3.2/topics/async/) 中阅读更多关于 Django 的异步支持，以及如何最好的使用异步视图。
