Checks 源码分析
Django 的 checks
模块提供了一系列的检查函数,用于检查 Django 项目的配置是否正确。
文件结构
.
├── __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.py3 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
用于注册检查。
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 **kwargsand 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# orregistry.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 = tagschecks = (self.deployment_checksif 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 机制来忽略特定的错误。
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 = levelself.msg = msgself.hint = hintself.obj = objself.id = iddef __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 modelsif 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.labelelse: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 >= leveldef is_silenced(self):from django.conf import settingsreturn self.id in settings.SILENCED_SYSTEM_CHECKSclass 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)