系统检查框架¶
系统检查框架是一组用于验证 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
编写测试¶
消息是可比较的。这使您可以轻松地编写测试
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
设置必须是一个整数,一个内置 来自站点框架的检查
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())