Apps 下主要有两个类: AppConfig
和Apps
.
apps/ # 应用目录
├── __init__.py # 应用初始化文件
├── config.py # AppConfig 类
├── registry.py # Apps 类
位于 apps/config.py 文件中, 主要用来定义应用的配置信息和初始化过后的过程函数。
name
: 应用的名称, 一般是应用的包名.module
: 应用的模块, 一般是应用的 __init__.py
文件.apps
: 应用管理器, 用来管理应用的注册和配置信息.label
: 应用的标签, 包名的最后一部分, 如django.contrib.admin
=> admin
.verbose_name
: 应用的显示名称, 一般是应用的名称的首字母大写形式.path
: 应用的路径, 一般是应用的包路径.models_module
: 应用的模型模块, 通过import_models
加载,在 app 初始化完成前,该属性为None
.models
: 应用的模型 mapping[str, models.Model], 通过import_models
加载,在 app 初始化完成前,该属性为None
.create()
: 类方法,返回一个AppConfig
实例,通过外部的 entry 字符串,加载目录下的 apps 中的子类并初始化返回,可以通过设置default
属性来控制实例化哪个AppConfig
子类。
class AppConfig:
...
@classmethod
def create(cls, entry):
"""
Factory that creates an app config from an entry in INSTALLED_APPS.
"""
# create() eventually returns app_config_class(app_name, app_module).
app_config_class = None
app_name = None
app_module = None
# If import_module succeeds, entry points to the app module.
try:
app_module = import_module(entry)
except Exception:
pass
else:
# If app_module has an apps submodule that defines a single
# AppConfig subclass, use it automatically.
# To prevent this, an AppConfig subclass can declare a class
# variable default = False.
# If the apps module defines more than one AppConfig subclass,
# the default one can declare default = True.
if module_has_submodule(app_module, APPS_MODULE_NAME):
mod_path = "%s.%s" % (entry, APPS_MODULE_NAME)
mod = import_module(mod_path)
# Check if there's exactly one AppConfig candidate,
# excluding those that explicitly define default = False.
app_configs = [
(name, candidate)
for name, candidate in inspect.getmembers(mod, inspect.isclass)
if (
issubclass(candidate, cls)
and candidate is not cls
and getattr(candidate, "default", True)
)
]
if len(app_configs) == 1:
app_config_class = app_configs[0][1]
else:
# Check if there's exactly one AppConfig subclass,
# among those that explicitly define default = True.
app_configs = [
(name, candidate)
for name, candidate in app_configs
if getattr(candidate, "default", False)
]
if len(app_configs) > 1:
candidates = [repr(name) for name, _ in app_configs]
raise RuntimeError(
"%r declares more than one default AppConfig: "
"%s." % (mod_path, ", ".join(candidates))
)
elif len(app_configs) == 1:
app_config_class = app_configs[0][1]
# Use the default app config class if we didn't find anything.
if app_config_class is None:
app_config_class = cls
app_name = entry
# If import_string succeeds, entry is an app config class.
if app_config_class is None:
try:
app_config_class = import_string(entry)
except Exception:
pass
# If both import_module and import_string failed, it means that entry
# doesn't have a valid value.
if app_module is None and app_config_class is None:
# If the last component of entry starts with an uppercase letter,
# then it was likely intended to be an app config class; if not,
# an app module. Provide a nice error message in both cases.
mod_path, _, cls_name = entry.rpartition(".")
if mod_path and cls_name[0].isupper():
# We could simply re-trigger the string import exception, but
# we're going the extra mile and providing a better error
# message for typos in INSTALLED_APPS.
# This may raise ImportError, which is the best exception
# possible if the module at mod_path cannot be imported.
mod = import_module(mod_path)
candidates = [
repr(name)
for name, candidate in inspect.getmembers(mod, inspect.isclass)
if issubclass(candidate, cls) and candidate is not cls
]
msg = "Module '%s' does not contain a '%s' class." % (
mod_path,
cls_name,
)
if candidates:
msg += " Choices are: %s." % ", ".join(candidates)
raise ImportError(msg)
else:
# Re-trigger the module import exception.
import_module(entry)
# Check for obvious errors. (This check prevents duck typing, but
# it could be removed if it became a problem in practice.)
if not issubclass(app_config_class, AppConfig):
raise ImproperlyConfigured("'%s' isn't a subclass of AppConfig." % entry)
# Obtain app name here rather than in AppClass.__init__ to keep
# all error checking for entries in INSTALLED_APPS in one place.
if app_name is None:
try:
app_name = app_config_class.name
except AttributeError:
raise ImproperlyConfigured("'%s' must supply a name attribute." % entry)
# Ensure app_name points to a valid module.
try:
app_module = import_module(app_name)
except ImportError:
raise ImproperlyConfigured(
"Cannot import '%s'. Check that '%s.%s.name' is correct."
% (
app_name,
app_config_class.__module__,
app_config_class.__qualname__,
)
)
# Entry is a path to an app config class.
return app_config_class(app_name, app_module)
ready()
: 实例方法,在应用初始化完成后调用,一般用来注册信号和其他初始化操作。all_models
: 存放app_label
.model_name
和Model
的 mapping, 在 Model.new中调用apps.register_model
完成注册。app_configs
: 存放AppConfig
实例的 mapping。stored_app_configs
: 栈存放当前状态apps_ready
: 标志位,表示是否已经完成应用初始化。models_ready
: 标志位,表示是否已经完成模型初始化。ready
: 标志位,表示是否已经完成初始化。_lock
: 锁,用来控制并发访问。loading
: 标志位,表示是否正在加载。_pending_operations
: 存放延迟 Model 注册的操作。应对模型的关联问题。populate()
: 实例方法,installed_apps
遍历初始化应用,如果已经是 AppConfig,则直接使用;否则调用create
方法创建.将初始化完成的 AppConfig 实例存入app_configs
中。在此确保app 名字不允许重复。# Phase 1: initialize app configs and import app modules.
for entry in installed_apps:
if isinstance(entry, AppConfig):
app_config = entry
else:
app_config = AppConfig.create(entry)
# 不能重名
if app_config.label in self.app_configs:
raise ImproperlyConfigured(
"Application labels aren't unique, "
"duplicates: %s" % app_config.label
)
self.app_configs[app_config.label] = app_config
app_config.apps = self
# Check for duplicate app names.
# 可以用counter来统计重复
counts = Counter(
app_config.name for app_config in self.app_configs.values()
)
duplicates = [name for name, count in counts.most_common() if count > 1]
if duplicates:
raise ImproperlyConfigured(
"Application names aren't unique, "
"duplicates: %s" % ", ".join(duplicates)
)
# Phase 2: import models modules.
for app_config in self.app_configs.values():
app_config.import_models()
self.clear_cache()
self.models_ready = True
# Phase 3: run ready() methods of app configs.
for app_config in self.get_app_configs():
# 调用每个appconfig的ready方法,完成自定义初始化步骤
app_config.ready()
self.ready = True
self.ready_event.set()
get_models()
: 实例方法,返回所有注册的模型列表,带缓存。
# This method is performance-critical at least for Django's test suite.
@functools.cache
def get_models(self, include_auto_created=False, include_swapped=False):
"""
Return a list of all installed models.
By default, the following models aren't included:
- auto-created models for many-to-many relations without
an explicit intermediate table,
- models that have been swapped out.
Set the corresponding keyword argument to True to include such models.
"""
self.check_models_ready()
result = []
for app_config in self.app_configs.values():
result.extend(app_config.get_models(include_auto_created, include_swapped))
return result