系統檢查框架¶
系統檢查框架是一組用於驗證 Django 專案的靜態檢查。它可以檢測常見問題,並提供如何修復這些問題的提示。此框架具有可擴展性,因此您可以輕鬆添加自己的檢查。
檢查可以透過 check
命令明確觸發。檢查會在大多數命令之前隱式觸發,包括 runserver
和 migrate
。基於效能考量,檢查不會作為部署中使用的 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
關鍵字參數。這是資料庫別名的清單,可以使用這些別名的連線來檢查資料庫層級的設定。如果 databases
為 None
,則檢查不得使用任何資料庫連線。
**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
的自訂欄位。此欄位會將 min
和 max
引數新增至 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
在舊版本中,範本引擎沒有實作 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())