目录
Apps模块
在 Django 中,将项目的结构方式组织为:项目(project) -> 应用(app)。后面统一以 app 代表应用。
一般我们创建一个 Django 项目会执行命令:django-admin startproject myproject
,这就创建了一个项目。
创建项目后,我们会在项目目录下执行命令:python manage.py startapp myApp
,创建一个名为 myApp 的app。
- 一个Django项目就是一个基于Django的Web应用。
- 一个Django项目包含若干个Django应用。
- 一个Django应用通常是一个功能模块,通常包含models, views, templates, template tags, static files, URLs等。
可以说,整个 Django 就是围绕一个个的 App 进行模块化项目的组织和开发的,Apps 是整个 Django 的核心和基础。
使用与添加
在项目的 settings 文件中,已经自带了一些默认的 app:
INSTALLED_APPS = [
'django.contrib.admin', 站点管理系统
'django.contrib.auth', 认证系统
'django.contrib.contenttypes', content types框架
'django.contrib.sessions', session框架
'django.contrib.messages', message框架
'django.contrib.staticfiles', 静态文件管理框架
]
INSTALLED_APPS 中的任何一个,都是一个 app,INSTALLED_APPS 是一个列表,我们可以在后面添加自己通过命令创建的 app 或者是第三方库的 app。
目录结构
源码中 apps 目录主要结构:
├── apps
│ ├── config.py # 与app的配置相关
│ └── registry.py # 与app本身的注册相关
config.py
中只有一个类:AppConfig,主要定义了一个 app 以及它相关的配置信息。当我们通过python manage.py startapp myApp
时,会在 django 根目录下生成 myApp 的目录,目录下有个 apps.py 文件:
from django.apps import AppConfig
class MyAppConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "myApp"
事实上,一个Django应用就是一个django.apps.AppConfig类的扩展子类。
registry.py
中包含了 INSTALLED_APPS 中所有的 app 加载配置及初始化相关的操作,确保 Django 启动时,所有的 app 都是正确加载的,里面有非常多的逻辑。
在深入研究 app 的作用之前,先看一下 Django 服务是如何启动起来的。
Django 是如何启动的
当我们启动一个 Django 服务时,会进行一系列的准备工作,只有这个准备过程是正常的,才能开始后续工作(接受web请求,执行命令行动作等)。
由于Django良好的设计,整个启动过程在一个函数中就完成了:django.setup()
,该函数位于 django/__init__.py
中:
def setup(set_prefix=True):
from django.apps import apps
from django.conf import settings
from django.urls import set_script_prefix
from django.utils.log import configure_logging
configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
if set_prefix:
set_script_prefix(
"/" if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME
)
apps.populate(settings.INSTALLED_APPS)
上面主要几步:
- 加载日志相关配置
- 配置 script_name
- 加载模块
from django.apps import apps
,注册所有的 app
其中最重要的就是注册 app 的操作,我们就从 apps.populate
以及 apps 的配置开始入手。
apps
查看 apps 的源码,其中最重要的就是:
- app 配置信息类 AppConfig。
- app 加载填充类 Apps。
配置信息类
初始化函数
一个 AppConfig类及其子类实例化后,就是一个 django 应用的实例,下面是初始化__init__
函数:
class AppConfig:
def __init__(self, app_name, app_module):
self.name = app_name # app_name就是settings.INSTALLED_APPS的一个值,比如 django.contrib.admin
# app对应的module对象,比如<module 'django.contrib.admin' from django/contrib/admin/__init__.py'>
self.module = app_module
self.apps = None # 加载填充类 Apps 的实例
# 设置 app 的标签,如无定义则为app全称最后的字符,比如 django.contrib.admin 的 label 为 admin。
if not hasattr(self, "label"):
self.label = app_name.rpartition(".")[2]
if not self.label.isidentifier():
raise ImproperlyConfigured(
"The app label '%s' is not a valid Python identifier." % self.label
)
# 对外显示的名称,默认为 label 的首字母大写格式。
if not hasattr(self, "verbose_name"):
self.verbose_name = self.label.title()
# module对象在文件系统中的绝对路径
if not hasattr(self, "path"):
self.path = self._path_from_module(app_module)
# app的 models 对象 <module 'django.contrib.admin.models' from 'xxx/django/contrib/admin/models.py'>
self.models_module = None
# 保存了这个app里面所有的Model类的映射,比如 {'logentry': <class 'django.contrib.admin.models.LogEntry'>}
self.models = None
AppConfig
需要传入两个参数 app_name 和 app_module ,分别是 app 的全称,以及 app 对应的 module 对象,初始化后生成 app 必要的一些配置参数。
其他方法
简化后的 AppConfig 类的主要方法如下:
class AppConfig:
# 返回传入的 module 对象的完整绝对路径
def _path_from_module(self,module):
pass
# 方法返回的就是 AppConfig 类的实例,内部有非常多异常的判断,具体不展示了。
@classmethod
def create(cls, entry):
pass
# 在 app 或者 model准备好后,根据 model_name 获取 app 下某个具体的 model 对象。
def get_model(self, model_name, require_ready=True):
pass
# 获取 app 下所有的 model 对象,返回的是一个生成器
def get_models(self, include_auto_created=False, include_swapped=False):
pass
# 增加app成功加载后的逻辑,这是django给我们提供的一个扩展点。
def ready(self):
pass
# 加载 app 的 模型,并传给 apps 实例的 all_models 属性。
def import_models(self):
pass
大致了解了 AppConfig 之后,我们再来看看配置填充类 Apps 。
配置填充类
初始化函数
Apps 的 __init__
函数中,主要是存了:
- 标志信息:app 、模型等配置是否填充准备就绪。
- all_models:所有 app 与 模型 的字典映射。
- app_configs:所有 app 与 app 对象的字典映射。
populate方法
populate 是 app 中最重要的核心代码,在 Django.setup() 中调用了下面展示了其中主要的代码,并在代码中标上了注释:
def populate(self, installed_apps=None):
if self.ready: # 如果已经加载过 install_apps 配置,直接返回
return
with self._lock: # 可能会被会两个并行的线程调用,需要加锁确保不会重复加载
if self.ready: # 多线程下,需要双重确认,避免重复执行配置加载
return
# 步骤1
# 循环 installed_apps 中的每项,实例化 AppConfig 类,存放在 self.app_configs 中
for entry in installed_apps:
if isinstance(entry, AppConfig):
app_config = entry
else:
app_config = AppConfig.create(entry)
# 防止同名 app 出现,检测唯一性
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配置存放在 self.app_configs 中
app_config.apps = self # AppConfig类中组合 Apps 的实例
# 同样是检测 app 唯一性
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)
)
self.apps_ready = True
# 阶段2
# 加载 app 下的所有模型
for app_config in self.app_configs.values():
app_config.import_models()
self.clear_cache()
self.models_ready = True
# 阶段3
# 每个 AppConfig 的扩展点,如果有需要可以通过 ready 方法执行一些定制化的操作
for app_config in self.get_app_configs():
app_config.ready()
self.ready = True
self.ready_event.set()
populate 方法将载入 app 的配置信息和模型划分为了三个步骤:
- app准备:循环
installed_apps
导入和初始化 AppConfig 类,存放在 self.app_configs 属性中,并确保没有同名的 app 。 - model准备:循环每个初始化后 AppConfig 类,加载 app 下的模型。
- app后置方法执行:循环 AppConfig 类,如果有实现 ready 方法的,就执行。
通过这三步,将installed_apps
中定义的 app 加载填充完成。
其他方法
apps 类中还有一些其他的方法,主要是一些辅助方法,这里不详细展开,大概列举一下作用:
get_app_configs
:获取所有的 app_config 实例对象,对应 get_app_config 获取单个实例。
get_models
:获取所有的模型,对应 get_model 获取单个模型。
stored_app_configs
:主要用在测试中。在测试中,我们经常需要改成可用的app或者还原回来等操作。具体可参考set_available_apps、set_installed_apps等方法中。