Django框架的源码解析

  • 简述
  • 从django-admin startproject [name]开始
    • 小结
  • django.core.management
    • init.py
        • 1. 5个方法
        • 2. ManagementUtility 类
      • 小结
    • base.py
        • 1. 2个方法:
        • 2. CommandError(Exception):
        • 3. SystemCheckError(CommandError):
        • 4. CommandParser(ArgumentParser):
        • 5. DjangoHelpFormatter(HelpFormatter):
        • 6. OutputWrapper(TextIOBase):
        • 7. BaseCommand:
        • 8. AppCommand(BaseCommand):
        • 9. LabelCommand(BaseCommand):
    • color.py
    • sql.py
        • def sql_flush(style, connection, only_django=False, reset_sequences=True, allow_cascade=False):
        • emit_pre_migrate_signal(verbosity, interactive, db, **kwargs):
        • emit_post_migrate_signal(verbosity, interactive, db, **kwargs):
    • templates.py
        • class TemplateCommand(BaseCommand):
    • utils.py
    • commands
        • check.py
        • compilemessages.py
        • createcachetable.py
        • dbshell.py
        • diffsettings.py
        • dumpdata.py
        • flush
        • inspectdb
        • loaddata
        • makemessages
        • makemigrations
        • migrate
        • runserver
        • sendtestemail
        • shell
        • showmigrations
        • sqlflush
        • sqlmigrate
        • sqlsequencereset
        • squashmigration
        • startapp
        • startproject
        • test
        • testserver
  • 总结

简述

在此写下我在阅读Django源码时的心得体会。
选择Django版本为3.1.3.
本人是一个python入门选手,这是我第一次阅读大型框架源码,并不知道该以何种顺序阅读。故此,阅读顺序完全按照个人习惯,从框架如何开始建立项目为头,慢慢解析。
解析中会穿插着我的一些个人总结,及一些需要后续深入了解展开解析的内容。
解析中的任何不足和错误,请大家积极指出。谢谢各位!

从django-admin startproject [name]开始

想建立一个django项目,第一步就是需要在终端使用 django-admin startproject project 命令生成一个django项目框架。
django-admin (命令的路径此处就不多说了。)在终端中找到命令路径,通过cat命令,可以看到下面内容:

$ cat django-admin

#!/home/MyProject/env/env-import-python3.6/bin/python3.6
# -*- coding: utf-8 -*-
import re
import sys
from django.core.management import execute_from_command_line
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(execute_from_command_line())

sys.arg:是sys模块的一个全局变量,也称sys模块的一个属性。argv本身为一个list类型的对象,该对象持有的第1个元素是命令行中传入的模块名、从第2个元素开始(含),均为命令行中传入的参数。
r’(-script.pyw|.exe)?$’ 这个正则挺简单的。作用就是把字符串中结尾处的 .exe 或者 -script.pyw 替换掉。因为不同系统下,文件名后缀不同,替换掉不同的部分,统一程序名。

execute_from_command_line 方法在core.management.init.py文件中。

Django框架的源码解析_第1张图片

def execute_from_command_line(argv=None):
    """Run a ManagementUtility."""
    utility = ManagementUtility(argv)
    utility.execute()

ManagementUtility 是封装的django-admin和manage.py程序的逻辑方法。
execute() 方法:获取命令行参数,找出需要运行的子命令,创建一个适合该命令的解析器,然后运行它。

Django框架的源码解析_第2张图片

需要运行的命令赋值给 subcommand 。
创建一个解析器实例。这个解析器的类CommandParser在core.management.base.py中,是基于ArgumentParser的。ArgumentParser位于python的argparse 模块,可以让人轻松编写用户友好的命令行接口。
创建解析器实例之后,对命令行传输过来的参数进行合理性判断,并预处理pythonpath和settings 加入到环境变量。

在运行django-admin 创建项目的时候,还没有自己配置的settings,故使用的是默认的conf下的global_settings.py。
确认了settings之后,进行校验,具体的校验代码在conf中。此处先不展开。
settings校验之后,判断要运行的子命令:此处分为runserver 命令和其他命令两个分支。(runserver后续展开)。
创建项目时,此处传入的命令是 startproject .
运行 django.setup()方法(在django.init.py)。

Django框架的源码解析_第3张图片

接下来是一个自动补全的调用 self.autocomplete(). 方法中说,如果用户不设置,就跳过。我没有使用过,这里就直接跳过了。(有用过的朋友,欢迎指教~)

然后是关于 help 和 version 的调用。如果命令行中有这些参数,会在终端输出关于该命令的帮助或版本信息。

最后,就是查找并执行我们输入的子命令了。

Django框架的源码解析_第4张图片

fetch_command()方法中,先用get_commands()检索所有的子命令模块。

Django框架的源码解析_第5张图片

get_commands()方法使用了python3.2之后新添加的lru_cache 装饰器。这是一个很好的缓存机制,有兴趣的朋友可以自行去了解。
get_commands()方法使用pkgutil (python的包管理工具模块)中的iter_modules方法返回一个{command_name: app_name}形式的字典。
方法中还对用户自己定义的commands方法进行了检索,这个是后话,容后再叙。

get_commands()返回了命令字典后,fetch_command()对传参进来的子命令进行校验,是否在已经定义了的命令中。

确认命令存在之后,判断该命令是否已经加载,如果已经加载,直接使用。如果没有,加载该命令并返回Command类。
(Commmand类继承自management.templates.py中的template类。template类又是继承自management.base.py中的BaseCommand类。)
调用实例化之后命令的run_from_argv()方法,设置所有要求的环境变量,进行系统自检。最终执行handle()方法,把conf下project_template目录中的所有内容,修改名称后缀之后复制到要创建项目的位置。

至此,项目创建完成,django-admin startproject 命令结束。

以上就是django-admin startproject [name] 命令的简略解析。

小结

通过对整个流程源码的阅读可以看出,django-admin startproject 命令其实简单来说就是 通过解析命令行传输进来的参数,把位于conf下的project_template文件夹内容更名之后复制到用户所设定的位置。
流程大部分的源码,都是django-admin 和 manage命令复用的。startproject 也只是诸多子命令中的一个,由此可见,我们在项目中,可以根据需求,自助封装子命令。具体方法我们后续解析。

其中涉及到的 BaseCommand,基于BaseCommand的TemplateCommand,需要再深入阅读。

django.core.management

解析完django-admin startproject 命令,我顺势整体解析coer.management模块。
这个模块主要是完成django-admin 和 manage 命令的各种功能。

该模块的目录结构如下:

Django框架的源码解析_第6张图片

init.py

这个文件中包含了5个方法和一个 ManagementUtility 类。

1. 5个方法
  • find_commands():传入一个地址参数,返回一个包含所有有效命令的列表。
  • load_command_class():传入应用名和命令名,返回一个Command类的实例。
  • get_commands():检索所有命令。django.core包下的命令必定会被检索到。如果项目中有用户自主封装的命令,也会被一并检索。返回一个{command_name: app_name}类型的字典。
  • call_command():这是一个私有接口,通过传入 命令名或命令实例 和 各种参数,来直接调用命令。
  • execute_from_command_line():实例化一个ManagementUtility ,并执行。django-admin和manage 中都是调用的这个方法。
2. ManagementUtility 类

包含4个方法

  • main_help_text():以字符串形式返回脚本的主帮助文档。
  • fetch_command():匹配命令,如果正确匹配上命令,返回命令实例。
  • autocomplete():
  • execute():验证命令并执行。创建一个CommandParser实例解析参数。其中当子命令为 runserver 时, 涉及到一个autoreload 模块。我们后续解析到utils工具模块的时候再看。
小结

从__init__.py的源码中,我们可以看出django对于命令行命令的解析和执行流程。按照这个流程,我们可以在项目中封装自己的命令。
流程:在已配置的app中,建立management包(需要包含__init__.py文件),在management包中在创建一个commands包(需要包含__init__.py文件),在commands包就可以创建属于我们自己的命令文件了(文件名即为命令名)。命令文件中必须包含一个基于BaseCommand(位于core.management.base)的Command类,这个类就是执行命令时所需要实例化的类。类中要包含 handle 方法。handle方法要接受 *args **options 参数。这个handle方法就是执行命令时最终调用的方法,也就是命令的逻辑内容。
按照上述流程创建好一个命令之后,可以在终端使用 python manage.py [name] 来调用。也可以在程序中使用call_command()这个私有api调用。

base.py

可被django-admin 和 manage.py 执行的命令的基类。
包含8个类和2个方法:

1. 2个方法:
  • handle_default_options(): 设置配置文件和pythonpath。
  • no_translations():一个装饰器,强制命令在禁用翻译的情况下运行。(translation模块我现在还不知道是干嘛的,后边解析。。)
2. CommandError(Exception):
  • 继承自Exception的一个命令异常类,重写了__init__方法,添加了一个returncode的参数。
3. SystemCheckError(CommandError):
4. CommandParser(ArgumentParser):
  • 自定义的ArgumentParser类,以改进某些错误消息并在某些情况下阻止SystemExit,因为当以编程方式调用命令时,SystemExit是不可接受的。
  • 多了两个参数,missing_args_message和called_from_command_line。
  • error() 方法中实现了如果命令是在命令行中执行的,出现异常则直接exit,如果是进程调用则抛出异常,至于是否退出,需要看类实例化调用处的处理。比如__init__.py ManagementUtility() 的 excute()方法中的如下代码则不退出:

Django框架的源码解析_第7张图片

5. DjangoHelpFormatter(HelpFormatter):
  • 自定义帮助格式化程序。
6. OutputWrapper(TextIOBase):
7. BaseCommand:

命令基类。

  • 几个可能影响流程中各个步骤的属性:
    help : 关于命令的简短描述,会在帮助信息中打印出来。
    output_transaction: 一个布尔类型的标志,标志命令是否输出SQL声明。默认为FALSE,如果设置为TRUE的话,会自动包装上 BEGIN; and COMMIT;
    requires_system_checks:布尔类型属性,默认为True,在执行命令之前,整个django项目将会检查错误。
    requires_migrations_checks:布尔类型属性,如果为TRUE,在磁盘上的migrations设置和数据库中的migrations设置不匹配时,会打印出一个warning。
    stealth_options:一个包含所有 命令会用到的 未被参数解析器定义的 可选参数。
  • get_version(): 返回django版本。
  • create_parser():创建解析器实例,并添加几个默认可选参数。
  • add_arguments():子类重写。
  • print_help():打印帮助信息。
  • run_from_argv():设置要求的环境变化,然后执行execute()。
  • execute():执行handle()方法。执行之前进行一系列检查。
  • check():使用core.checks模块,对整个django项目检查错误。
  • check_migrations():当在磁盘上的migrations设置和数据库中的migrations设置不匹配时,打印出一个warning。
  • handle():子类中需重写此方法,未重写的话,会raise异常。
8. AppCommand(BaseCommand):

app命令基类,继承自BaseCommand.

  • add_arguments():重写基类中的add_arguments(),执行parser对象的add_argument方法添加一个 nargs='+'的参数,表示在命令行中可以输入一个或多个应用程序标签。
  • handle():引入django.apps 模块,使用模块中的get_app_config()方法,对命令行中传输进来的args参数列表进行遍历,返回应用程序的配置list,然后遍历应用程序的配置list,逐个传入handle_app_config方法并执行。返回结果。
  • handle_app_config():子类中需重写的方法。未重写的话,会raise异常。
9. LabelCommand(BaseCommand):

label命令基类,继承自BaseCommand.

  • add_arguments():重写基类中的add_arguments(),执行parser对象的add_argument方法添加一个 nargs='+'的参数,表示在命令行中可以输入一个或多个标签。
  • handle():遍历传入的labels列表,执行handle_label方法。返回结果。
  • handle_label():子类中需重写的方法。未重写的话,会raise异常。

color.py

这个模块是用来设置终端颜色显示方案。包含4个方法,一个类。
如果环境变量中没有设置DJANGO_COLORS变量,使用django.utils.termcolors的颜色配置。

sql.py

def sql_flush(style, connection, only_django=False, reset_sequences=True, allow_cascade=False):

返回一个用来刷新数据库的sql语句列表。

emit_pre_migrate_signal(verbosity, interactive, db, **kwargs):

为每个应用程序发出预迁移信号。

emit_post_migrate_signal(verbosity, interactive, db, **kwargs):

为每个应用程序发出迁移后信号。

templates.py

class TemplateCommand(BaseCommand):

此类是用来复制Django app 布局模板或Django project 布局模板到指定的目录中,来创建app或project。
此中包含9个方法。

  • add_arguments(self, parser):添加命令行参数,包括name,directory,–template,–extension,–name
  • handle(self, app_or_project, name, target=None, **options):主要执行的创建app或project的逻辑方法。
  • handle_template(self, template, subdir):明确app或project模板的地址。
  • validate_name(self, name, name_or_dir=‘name’):验证name,并查看是否可以导入,如果可以导入,证明已存在,raise异常。
  • download(self, url):下载传入的url文件,并返回文件名。
  • splitext(self, the_path):分割传入的文件路径。类似os.path.splitext,增加了对tar文件的特殊处理。
  • extract(self, filename):将给定的文件临时解压缩到,并返回包含提取内容的目录的路径。
  • is_url(self, template): 如果template的地址格式类似url则返回True,
  • make_writeable(self, filename):确保文件是可写的。

utils.py

management的工具集。这里就不展开了,都是很简单的方法。

commands

所有django 内置的子命令。

check.py

检查整个django项目中可能存在的错误。
这个命令引入了core.checks模块。

compilemessages.py

这个命令是Django官方提供的项目国际化命令,这个命令需要配合makemessages命令使用。makemessages命令生成一个po文件,compilemessages把po文件编译生成mo文件。
具体国际化的使用方式,我从网上浏览学习的时候发现了这个文章,介绍的很不错。https://www.jb51.net/article/143337.htm大家有兴趣的可以去学习。

createcachetable.py

当项目的缓存方式使用数据库缓存时,执行此命令自动生成缓存使用数据库表。python manage.py createcachetable

dbshell.py

python manage.py dbshell
Django 会自动进入在settings.py中设置的数据库,如果是 MySQL 或 postgreSQL,会要求输入数据库用户密码。

diffsettings.py

我们在使用djnago项目的时候,都会根据需要,配置项目中settings.py。Django默认的settings是django.conf.global_settings.py。如果我们需要对照我们写的settings与默认settings的区别,可以使用此命令。
django在编译时,先载入global_settings.py中的配置,然后加载指定的settings文件,重写改变的设定。
在其他module中,如果希望访问settings文件,可以使用from django.conf import settings来导入。不要导入global_settings或者我们自己写的settings。因为,django.conf.setting提取了global_settings和我们自己写的settings里面的内容。相比直接导入自己写的settings文件和global_settings文件,它提供给我们的是一个接口。可以实现解耦的作用。

dumpdata.py

此命令用于导出项目数据库中的数据
python manage.py dumpdata -o /home/b.json accounts.User --indent 2
-o 后面接输出文件路径
后边的accounts.User 是app名和model名
–indent 可以设置导出的格式。

flush

此命令可以清空数据库中所有数据,留下空表

inspectdb

Django用来反向生成Model的方法。此命令可以把配置中连接的数据库中的数据表,反向生成在指定app下的model中。
python manage.py inspecdb > [your app name]\models.py

loaddata

主要用于单元测试时测试数据的导入。

makemessages

国际化命令,见上compilemessages命令。

makemigrations

python manage.py makemigrations这个命令是记录我们对models.py的所有改动,并且将这个改动迁移到migrations这个文件下生成一个文件例如:0001文件。

migrate

此命令是把执行makemigrations之后所记录的改动,执行到数据库。

runserver

开发测试时常用到的,用来在本地通过Django内置服务器启动项目。

sendtestemail

发送测试邮件,验证配置中的邮箱是否有效。

shell

python manage shell 命令,将自动帮你处理DJANGO_SETTINGS_MODULE。这样在命令行中,即可直接对项目进行操作无需在配置环境变量。

showmigrations

执行python manage showmigrations 命令,将在终端看到当前项目可以获得的所有迁移信息。

sqlflush

打印出将数据库中的所有表返回到刚安装后的状态所需的SQL语句列表。即如何实现的flush命令。

sqlmigrate

打印出 migration 所需执行的sql语句。

sqlsequencereset

打印用于顺序重置给定应用程序名称的SQL语句。

squashmigration

压缩合并迁移文件

startapp

新建一个app。

startproject

新建一个项目。

test

用来测试项目模块功能。命令行中需输入要测试的功能模块
python manage.py test …

testserver

启用一个测试开发服务,命令行输入fixture路径来提供需要用到数据。

总结

core.management 是管理Django命令的模块。其中包含的内置命令是用来快速搭建项目并为开发者提供一些便捷的开发测试环境。
在准备使用这一模块建立属于自己的命令或者想对源码有深入的理解,需要提前熟悉以下Python库:
argparse,concurrent.futures。
整个management模块简单理解其实就是基于argparse库的二次开发。

你可能感兴趣的:(python,django,python,后端)