Django源码Apps模块

目录

Django学习

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本身的注册相关
  1. 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类的扩展子类。

  1. 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)

上面主要几步:

  1. 加载日志相关配置
  2. 配置 script_name
  3. 加载模块from django.apps import apps ,注册所有的 app

其中最重要的就是注册 app 的操作,我们就从 apps.populate 以及 apps 的配置开始入手。

apps

查看 apps 的源码,其中最重要的就是:

  1. app 配置信息类 AppConfig。
  2. 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__函数中,主要是存了:

  1. 标志信息:app 、模型等配置是否填充准备就绪。
  2. all_models:所有 app 与 模型 的字典映射。
  3. 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 的配置信息和模型划分为了三个步骤:

  1. app准备:循环 installed_apps导入和初始化 AppConfig 类,存放在 self.app_configs 属性中,并确保没有同名的 app 。
  2. model准备:循环每个初始化后 AppConfig 类,加载 app 下的模型。
  3. 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等方法中。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tlqwanttolearnit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值