系統檢查框架

系統檢查框架是一組用於驗證 Django 專案的靜態檢查。它可以檢測常見問題,並提供如何修復這些問題的提示。此框架具有可擴展性,因此您可以輕鬆添加自己的檢查。

檢查可以透過 check 命令明確觸發。檢查會在大多數命令之前隱式觸發,包括 runservermigrate。基於效能考量,檢查不會作為部署中使用的 WSGI 堆疊的一部分執行。如果您需要在部署伺服器上執行系統檢查,請使用 check 明確觸發它們。

嚴重的錯誤會阻止 Django 命令(例如 runserver)完全執行。次要問題會報告到主控台。如果您已檢查過警告的原因,並且願意忽略它,您可以使用專案設定檔中的 SILENCED_SYSTEM_CHECKS 設定來隱藏特定警告。

您可以在 系統檢查參考 中找到 Django 可以發出的所有檢查的完整清單。

編寫您自己的檢查

此框架具有彈性,允許您編寫函數來執行您可能需要的任何其他類型的檢查。以下是一個範例存根檢查函數

from django.core.checks import Error, register


@register()
def example_check(app_configs, **kwargs):
    errors = []
    # ... your check logic here
    if check_failed:
        errors.append(
            Error(
                "an error",
                hint="A hint.",
                obj=checked_object,
                id="myapp.E001",
            )
        )
    return errors

檢查函數必須接受 app_configs 參數;此參數是要檢查的應用程式清單。如果 None,則必須對專案中所有已安裝的應用程式執行檢查。

檢查將會收到 databases 關鍵字參數。這是資料庫別名的清單,可以使用這些別名的連線來檢查資料庫層級的設定。如果 databasesNone,則檢查不得使用任何資料庫連線。

**kwargs 參數是未來擴充所必需的。

訊息

該函數必須傳回訊息清單。如果檢查沒有發現任何問題,則檢查函數必須傳回空清單。

檢查方法引發的警告和錯誤必須是 CheckMessage 的實例。CheckMessage 的實例封裝了單一可回報的錯誤或警告。它還提供適用於該訊息的上下文和提示,以及用於篩選的唯一識別碼。

這個概念與 訊息框架記錄框架 中的訊息非常相似。訊息會以 level 來標記,表示訊息的嚴重性。

還有一些捷徑可以更輕鬆地建立具有常見層級的訊息。使用這些類別時,您可以省略 level 參數,因為它由類別名稱隱含。

註冊和標記檢查

最後,您的檢查函數必須明確註冊到系統檢查註冊表中。檢查應在應用程式載入時載入的檔案中註冊;例如,在 AppConfig.ready() 方法中。

register(*tags)(function)

您可以將任意數量的標籤傳遞給 register,以標記您的檢查。標記檢查很有用,因為它允許您僅執行特定群組的檢查。例如,要註冊相容性檢查,您將進行以下呼叫

from django.core.checks import register, Tags


@register(Tags.compatibility)
def my_check(app_configs, **kwargs):
    # ... perform compatibility checks and collect errors
    return errors

您可以註冊僅與生產設定檔相關的「部署檢查」,如下所示

@register(Tags.security, deploy=True)
def my_check(app_configs, **kwargs): ...

只有在使用 check --deploy 選項時,才會執行這些檢查。

您也可以使用 register 作為函數,而不是裝飾器,方法是將可呼叫的物件(通常是函數)作為 register 的第一個參數傳遞。

下面的程式碼與上面的程式碼等效

def my_check(app_configs, **kwargs): ...


register(my_check, Tags.security, deploy=True)

欄位、模型、管理員、範本引擎和資料庫檢查

在某些情況下,您不需要註冊檢查函數 - 您可以依附在現有的註冊上。

欄位、模型、模型管理員、範本引擎和資料庫後端都實作了已向檢查框架註冊的 check() 方法。如果您想要新增額外的檢查,您可以擴充基礎類別上的實作,執行您需要的任何額外檢查,並將任何訊息附加到基礎類別產生的訊息。建議您將每個檢查委派給個別的方法。

考慮一個範例,您正在實作名為 RangedIntegerField 的自訂欄位。此欄位會將 minmax 引數新增至 IntegerField 的建構函式。您可能想要新增一個檢查,以確保使用者提供的最小值小於或等於最大值。以下程式碼片段顯示您可以如何實作此檢查

from django.core import checks
from django.db import models


class RangedIntegerField(models.IntegerField):
    def __init__(self, min=None, max=None, **kwargs):
        super().__init__(**kwargs)
        self.min = min
        self.max = max

    def check(self, **kwargs):
        # Call the superclass
        errors = super().check(**kwargs)

        # Do some custom checks and add messages to `errors`:
        errors.extend(self._check_min_max_values(**kwargs))

        # Return all errors and warnings
        return errors

    def _check_min_max_values(self, **kwargs):
        if self.min is not None and self.max is not None and self.min > self.max:
            return [
                checks.Error(
                    "min greater than max.",
                    hint="Decrease min or increase max.",
                    obj=self,
                    id="myapp.E001",
                )
            ]
        # When no error, return an empty list
        return []

如果您想要將檢查新增至模型管理員,您將在 Manager 的子類別上採用相同的方法。

如果您想要將檢查新增至模型類別,則方法幾乎相同:唯一的區別是檢查是類別方法,而不是實例方法

class MyModel(models.Model):
    @classmethod
    def check(cls, **kwargs):
        errors = super().check(**kwargs)
        # ... your own checks ...
        return errors
在 Django 5.1 中變更

在舊版本中,範本引擎沒有實作 check() 方法。

編寫測試

訊息是可比較的。這讓您可以輕鬆地編寫測試

from django.core.checks import Error

errors = checked_object.check()
expected_errors = [
    Error(
        "an error",
        hint="A hint.",
        obj=checked_object,
        id="myapp.E001",
    )
]
self.assertEqual(errors, expected_errors)

編寫整合測試

鑑於在應用程式載入時需要註冊某些檢查,測試它們在系統檢查框架內的整合會很有用。可以使用 call_command() 函數來完成此操作。

例如,此測試示範 SITE_ID 設定必須是整數,這是來自 sites 框架的內建 檢查

from django.core.management import call_command
from django.core.management.base import SystemCheckError
from django.test import SimpleTestCase, modify_settings, override_settings


class SystemCheckIntegrationTest(SimpleTestCase):
    @override_settings(SITE_ID="non_integer")
    @modify_settings(INSTALLED_APPS={"prepend": "django.contrib.sites"})
    def test_non_integer_site_id(self):
        message = "(sites.E101) The SITE_ID setting must be an integer."
        with self.assertRaisesMessage(SystemCheckError, message):
            call_command("check")

考慮以下檢查,如果名為 ENABLE_ANALYTICS 的自訂設定未設定為 True,則在部署時發出警告

from django.conf import settings
from django.core.checks import Warning, register


@register("myapp", deploy=True)
def check_enable_analytics_is_true_on_deploy(app_configs, **kwargs):
    errors = []
    if getattr(settings, "ENABLE_ANALYTICS", None) is not True:
        errors.append(
            Warning(
                "The ENABLE_ANALYTICS setting should be set to True in deployment.",
                id="myapp.W001",
            )
        )
    return errors

鑑於此檢查不會引發 SystemCheckError,因此可以像這樣斷言 stderr 輸出中是否存在警告訊息

from io import StringIO

from django.core.management import call_command
from django.test import SimpleTestCase, override_settings


class EnableAnalyticsDeploymentCheckTest(SimpleTestCase):
    @override_settings(ENABLE_ANALYTICS=None)
    def test_when_set_to_none(self):
        stderr = StringIO()
        call_command("check", "-t", "myapp", "--deploy", stderr=stderr)
        message = (
            "(myapp.W001) The ENABLE_ANALYTICS setting should be set "
            "to True in deployment."
        )
        self.assertIn(message, stderr.getvalue())
返回頂部