热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Django源码解析(1)

简述在此写下我在阅读Django源码时的心得体会。选择Django版本为3.1.3.本人是一个python入门选手,这是我第一次阅读大型框架源码,并不知道该以何种顺序阅读。故

目录
  • 简述
  • 从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文件中。

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

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

需要运行的命令赋值给 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)。

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

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

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

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

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 命令的各种功能。

该模块的目录结构如下:

__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()方法中的如下代码则不退出:

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库的二次开发。


推荐阅读
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
author-avatar
六月的
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有