撰寫視圖

視圖函式(簡稱視圖)是一個 Python 函式,它接收一個 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 lang="en"><body>It is now %s.</body></html>' % now
    return HttpResponse(html)

讓我們逐行查看此程式碼

  • 首先,我們從 django.http 模組匯入 HttpResponse 類別,以及 Python 的 datetime 函式庫。

  • 接下來,我們定義一個名為 current_datetime 的函式。這是視圖函式。每個視圖函式都會接收一個 HttpRequest 物件作為其第一個參數,通常命名為 request

    請注意,視圖函式的名稱並不重要;它不需要以特定方式命名才能讓 Django 識別它。我們在這裡將其命名為 current_datetime,因為該名稱清楚地表明了它的作用。

  • 視圖返回一個包含產生回應的 HttpResponse 物件。每個視圖函式都負責返回一個 HttpResponse 物件。(有一些例外,但我們稍後會討論這些例外。)

Django 的時區

Django 包含一個預設為 America/ChicagoTIME_ZONE 設定。這可能不是你居住的地方,因此你可能需要在你的設定檔中更改它。

將 URL 對應到視圖

總結一下,這個視圖函式返回一個包含當前日期和時間的 HTML 頁面。要在特定的 URL 顯示此視圖,你需要建立一個 URLconf;請參閱 URL 分派器 以取得說明。

返回錯誤

Django 提供幫助返回 HTTP 錯誤代碼。除了 200(表示「OK」)之外,還有許多常見 HTTP 狀態代碼的 HttpResponse 子類別。你可以在 請求/回應 文件中找到可用的子類別的完整清單。返回這些子類別之一的實例,而不是普通的 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 文件中所述,你也可以將 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)時,你負責定義結果錯誤頁面的 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 設定為 False 時,將會提供此範本。

DEBUGTrue 時,你可以向 Http404 提供訊息,它將會出現在標準的 404 偵錯範本中。將這些訊息用於偵錯目的;它們通常不適合在生產環境的 404 範本中使用。

自訂錯誤視圖

Django 中的預設錯誤視圖應足以滿足大多數 Web 應用程式的需求,但如果需要任何自訂行為,可以輕鬆地覆寫它們。在你的 URLconf 中指定處理常式,如下所示(將它們設定在其他任何地方都不會產生任何效果)。

page_not_found() 視圖會被 handler404 覆寫。

handler404 = "mysite.views.my_custom_page_not_found_view"

server_error() 視圖會被 handler500 覆寫。

handler500 = "mysite.views.my_custom_error_view"

permission_denied() 視圖會被 handler403 覆寫。

handler403 = "mysite.views.my_custom_permission_denied_view"

bad_request() 視圖會被 handler400 覆寫。

handler400 = "mysite.views.my_custom_bad_request_view"

另請參閱

使用 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)

非同步視圖

除了同步函式之外,視圖也可以是非同步(「async」)函式,通常使用 Python 的 async def 語法定義。Django 會自動偵測到這些函式並在非同步內容中執行它們。但是,你需要使用基於 ASGI 的非同步伺服器才能獲得它們的效能優勢。

以下是一個非同步視圖的範例

import datetime
from django.http import HttpResponse


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

你可以在 非同步支援 中閱讀更多關於 Django 的非同步支援,以及如何最佳使用非同步視圖的資訊。

返回頂端