Checks 源码分析
Django 的 checks
模块提供了一系列的检查函数,用于检查 Django 项目的配置是否正确。
文件结构
shell
.
├── __init__.py
├── async_checks.py
├── caches.py
├── compatibility
│ ├── __init__.py
│ └── django_4_0.py
├── database.py
├── files.py
├── messages.py
├── model_checks.py
├── registry.py
├── security
│ ├── __init__.py
│ ├── base.py
│ ├── csrf.py
│ └── sessions.py
├── templates.py
├── translation.py
└── urls.py
3 directories, 17 files
可以学习的地方
-
对项目的可靠性检查方面可以借鉴 Django 的设计,可以包括
- 内置的 contrib 如 admin
- async_support 特性检查,如是否在不支持的环境下开启了异步支持
- 缓存配置检查,如是否使用了过期时间短的缓存
- 数据库配置检查,如是否使用了过期时间短的数据库连接
- 模型检查,如是否有重复的模型名称
- 安全检查,如是否开启了 CSRF 保护,是否使用了过期时间短的 session
- 模板检查
- 翻译检查
- URL 检查
- siangls 检查
- files 文件系统检查
-
使用
inspect.func_accepts_kwargs
函数,强制让函数参数中包含**kwargs
同样的函数还有
func_accepts_var_args
,强制让函数参数中包含*args
func_supports_parameter
置顶 -
在
core/checks/registry.py
CheckRegistry 类方法提供了利用装饰器机制封装了函数register
用于注册检查。
python
class CheckRegistry:
def __init__(self):
self.registered_checks = set()
self.deployment_checks = set()
def register(self, check=None, *tags, **kwargs):
"""
Can be used as a function or a decorator. Register given function
`f` labeled with given `tags`. The function should receive **kwargs
and return list of Errors and Warnings.
Example::
registry = CheckRegistry()
@registry.register('mytag', 'anothertag')
def my_check(app_configs, **kwargs):
# ... perform checks and collect `errors` ...
return errors
# or
registry.register(my_check, 'mytag', 'anothertag')
"""
def inner(check):
if not func_accepts_kwargs(check):
raise TypeError(
"Check functions must accept keyword arguments (**kwargs)."
)
check.tags = tags
checks = (
self.deployment_checks
if kwargs.get("deploy")
else self.registered_checks
)
checks.add(check)
return check
# 如果你的check函数是一个callable对象,那么直接返回inner(check)
if callable(check):
return inner(check)
else:
if check:
tags += (check,)
return inner
- 为项目建立一个 runtime 检查机制,通过这些 checks 来确保项目的运行正确。
id 可以确保所有 errors 和 warnings 的唯一性,并提供一种过滤 id 和 level 机制来忽略特定的错误。
python
class CheckMessage:
def __init__(self, level, msg, hint=None, obj=None, id=None):
if not isinstance(level, int):
raise TypeError("The first argument should be level.")
self.level = level
self.msg = msg
self.hint = hint
self.obj = obj
self.id = id
def __eq__(self, other):
return isinstance(other, self.__class__) and all(
getattr(self, attr) == getattr(other, attr)
for attr in ["level", "msg", "hint", "obj", "id"]
)
def __str__(self):
from django.db import models
if self.obj is None:
obj = "?"
elif isinstance(self.obj, models.base.ModelBase):
# We need to hardcode ModelBase and Field cases because its __str__
# method doesn't return "applabel.modellabel" and cannot be changed.
obj = self.obj._meta.label
else:
obj = str(self.obj)
id = "(%s) " % self.id if self.id else ""
hint = "\n\tHINT: %s" % self.hint if self.hint else ""
return "%s: %s%s%s" % (obj, id, self.msg, hint)
def __repr__(self):
return "<%s: level=%r, msg=%r, hint=%r, obj=%r, id=%r>" % (
self.__class__.__name__,
self.level,
self.msg,
self.hint,
self.obj,
self.id,
)
def is_serious(self, level=ERROR):
return self.level >= level
def is_silenced(self):
from django.conf import settings
return self.id in settings.SILENCED_SYSTEM_CHECKS
class Debug(CheckMessage):
def __init__(self, *args, **kwargs):
super().__init__(DEBUG, *args, **kwargs)
class Info(CheckMessage):
def __init__(self, *args, **kwargs):
super().__init__(INFO, *args, **kwargs)
class Warning(CheckMessage):
def __init__(self, *args, **kwargs):
super().__init__(WARNING, *args, **kwargs)
class Error(CheckMessage):
def __init__(self, *args, **kwargs):
super().__init__(ERROR, *args, **kwargs)
class Critical(CheckMessage):
def __init__(self, *args, **kwargs):
super().__init__(CRITICAL, *args, **kwargs)