Django包含一个由已安装应用组成的注册表,它保存应用的配置并提供自省。 它还维护一个可用的模型的列表。
这个注册表叫做apps
,位于django.apps
中:
>>> from django.apps import apps
>>> apps.get_app_config('admin').verbose_name
'Administration'
术语项目描述Django一个Web应用。 项目中的Python包主要由settings模块定义,但通常包含其它内容。 例如,当你运行django-admin startproject mysite
你将获得一个名为mysite
的项目目录,它包含一个名为mysite
的Python包,其中有settings.py
、urls.py
和wsgi.py
文件。 项目的包通常被扩展成包含与特定应用不是紧密联系的东西,如fixture、CSS和模板。
通常,项目的根目录(包含manage.py
的目录)通常是一个项目所有应用的容器,这些应用不会独立安装。
应用这个词表示一个Python包,它提供某些功能的集合。 应用可以在多个项目中重用。
应用包含模型、视图、模板、模板标签、静态文件、URL、中间件等等。 它们通常通过INSTALLED_APPS
设置和其它机制例如URLconf、MIDDLEWARE
设置或模板继承连接到项目中。
Django的应用只是一个代码集,它与框架的其它部分进行交互,理解这点很重要。 没有类似Application
对象这样的一个东西。 然而,有一些地方Django需要与安装的应用交互,主要用于配置和自省。 这就是为什么应用的注册表要在AppConfig
实例中维护每个安装的应用的元数据。
项目的包本身也可以视为一个应用并有模型等, (这将需要将其添加到INSTALLED_APPS
)。
要配置一个应用,请子类化AppConfig
并将这个子类的点分路径放在INSTALLED_APPS
中。
当INSTALLED_APPS
包含一个应用模块的路径后,Django将在这个模块中检查一个default_app_config
变量。
如果这个变量有定义,它应该是这个应用的AppConfig
子类的路径。
如果没有default_app_config
,Django将使用AppConfig
基类。
default_app_config
允许早于Django 1.7的应用可以选择加入(如django.contrib.admin
)AppConfig
中的功能,而不需要用户更新他们的INSTALLED_APPS
。
新的应用应避免使用default_app_config
。 相反,他们应该要求在INSTALLED_APPS
中明确配置AppConfig
子类的正确的点分路径。
如果你正在创建一个名叫“Rock ’n’ roll”的可插式应用,下面展示如何给管理后台提供一个合适的名字:
# rock_n_roll/apps.py
from django.apps import AppConfig
class RockNRollConfig(AppConfig):
name = 'rock_n_roll'
verbose_name = "Rock ’n’ roll"
你可以让你的应用默认以下面的方式加载这个AppConfig
的子类:
# rock_n_roll/__init__.py
default_app_config = 'rock_n_roll.apps.RockNRollConfig'
这将使得INSTALLED_APPS
只包含'rock_n_roll'
时将使用RockNRollConfig
。 这允许你可以利用AppConfig
的功能而不用要求你的用户更新他们的INSTALLED_APPS
设置。 除了这种用例,最好避免使用default_app_config
,而是按如下所述在INSTALLED_APPS
中指定app config类。
当然,你也可以告诉你的用户将'rock_n_roll.apps.RockNRollConfig'
放在他们的INSTALLED_APPS
设置中。 你甚至可以提供几个具有不同行为的AppConfig
子类,并让使用者通过他们的 INSTALLED_APPS
设置选择。
建议的惯例做法是将配置类放在应用的apps
子模块中。 但是,Django 不强制这一点。
你必须包含name
属性来让Django决定该配置适用的应用。 你可以定义AppConfig
API 参考中记录的任何属性。
注
如果你的代码在应用的__init__.py
中导入应用程序注册表,名称apps
将与apps
子模块发生冲突。
最佳做法是将这些代码移到子模块,并将其导入。 一种解决方法是以一个不同的名称导入注册表︰
from django.apps import apps as django_apps
如果你在名为anthology
的项目中使用“Rock ’n’ roll”,但你希望将其显示为“Jazz Manouche”,则可以提供自己的配置:
# anthology/apps.py
from rock_n_roll.apps import RockNRollConfig
class JazzManoucheConfig(RockNRollConfig):
verbose_name = "Jazz Manouche"
# anthology/settings.py
INSTALLED_APPS = [
'anthology.apps.JazzManoucheConfig',
# ...
]
再说一次,在apps
子模块定义特定于项目的配置类是一项惯例,并不要求。
AppConfig.
name
¶应用的完整Python路径,例如'django.contrib.admin'
。
这个属性定义配置适用于哪个应用。 它必须在所有 AppConfig
子类中设置。
它在整个Django 项目中必须是唯一的。
AppConfig.
label
¶应用的缩写名称,例如'admin'
。
此属性允许重新标记应用,当两个应用程序有冲突的标签。 它默认为name
的最后一部分。
它应该是一个有效的 Python 标识符。
它在整个Django 项目中必须是唯一的。
AppConfig.
verbose_name
¶应用的适合阅读的名称,例如“Administration”。
此属性默认为label.title()
。
AppConfig.
module
¶应用的根模块,例如<module 'django.contrib.admin' from 'django/contrib/admin/__init__.pyc'>
。
AppConfig.
models_module
¶包含模型的模块,例如<module 'django.contrib.admin.models' from 'django/contrib/admin/models.pyc'>
。
如果应用不包含models
模块,它可能为None
。
注意与数据库相关的信号,如 pre_migrate
和 post_migrate
只有在应用具有models
模块时才发出。
AppConfig.
get_model
(model_name, require_ready=True)[source]¶返回具有给定 model_name
的 Model
。 model_name
是不区分大小写。
如果此应用程序中没有此类模型,则引发LookupError
需要完全填充应用注册表,除非require_ready
参数设置为False
。 require_ready
的行为与apps.get_model()
完全相同。
添加了require_ready
关键字参数。
AppConfig.
ready
()[source]¶子类可以重写此方法以执行初始化任务,如注册信号。 它在注册表填充完之后立即调用。
虽然您不可以在定义AppConfig
的模块级别上导入模型,但是您可以通过import
语句或者get_model()
在ready()
中导入他们.
如果您正在注册model signals
,则可以使用字符串标签来引用发件人,而不是使用模型类本身。
例如:
from django.db.models.signals import pre_save
def ready(self):
# importing model classes
from .models import MyModel # or...
MyModel = self.get_model('MyModel')
# registering signals with the model's string label
pre_save.connect(receiver, sender='app_label.MyModel')
警告
虽然你可以如上文所述访问模型类,请避免在你的ready()
实现中与数据库交互。
这包括执行查询的模型方法(save()
,delete()
,管理方法等),和通过django.db.connection
进行原始SQL查询。 你的ready()
方法将在每个管理命令启动期间运行。 例如,即使测试数据库配置与生产设置是分离的,manage.py test
仍会执行一些针对您的 production 数据库的查询 !
注
在常规的初始化过程中,ready
方法只由Django 调用一次。 但在一些极端情况下,特别是在欺骗安装好的应用的测试,ready
可能被调用不止一次。 在这种情况下,编写幂等方法,或者在AppConfig
类上放置一个标识来防止应该执行一次的代码重复运行。
Python版本3.3和更高版本支持不包含__init__.py
文件的Python包。 这些包称为“命名空间包”,可以分布在sys.path
(请参阅 PEP 420)上的不同位置的多个目录中。
Django应用需要文件系统的一个基本路径,其中Django(根据配置)将搜索模板、静态文件等。 因此,只有满足以下条件之一,命名空间包才能是Django应用:
如果这些条件都不满足,Django将产生ImproperlyConfigured
。
apps
¶应用的注册表提供下列公共API。 没有在下面列出的方法被认为是私有的,恕不另行通知。
apps.
ready
¶注册表完全填充后设置为True
的布尔属性,并调用所有AppConfig.ready()
方法。
apps.
get_app_config
(app_label)¶返回具有给定app_label
的AppConfig
。 如果没有这种模型存在,抛出LookupError
。
apps.
is_installed
(app_name)¶检查注册表中是否存在具有给定名称的应用。
app_name
是应用的完整名称,例如'django.contrib.admin'
。
apps.
get_model
(app_label, model_name, require_ready=True)¶返回具有给定app_label
和model_name
的Model
。 作为快捷方式,此方法还接受app_label.model_name
形式的一个单一参数。 model_name
是不区分大小写。
如果没有这种模型存在,抛出LookupError
。 使用不包含一个点的单个参数调用时将引发ValueError
。
需要完全填充应用注册表,除非require_ready
参数设置为False
。
将require_ready
设置为False
允许在应用注册表正在填充 t4>时查找while the app registry is being populated 那么get_model()
与导入模型的效果相同。 主要用例是使用AUTH_USER_MODEL
等设置配置模型类。
当require_ready
为False
时,get_model()
返回可能不完全正常运行的模型类(反向访问者可能会丢失),直到应用程序注册表已完全填充。 因此,最好将require_ready
保留到默认值True
。
添加了require_ready
关键字参数。
当Django 启动时,django.setup()
负责填充应用注册表。
setup
(set_prefix=True)[source]¶配置Django:
set_prefix
为True,则将URL解析器脚本前缀设置为FORCE_SCRIPT_NAME
,否则为/
。设置URL解析器脚本前缀的功能是新的。
自动调用此函数︰
在其他情况下它必须显式调用,例如在普通的 Python 脚本中。
应用注册表初始化分三个阶段。 在每个阶段,Django 以INSTALLED_APPS
中的顺序处理所有应用。
首先,Django 会导入INSTALLED_APPS
中的所有应用。
如果它是一个应用配置类,Django 导入应用的根包,通过其name
属性。 如果它是一个Python 包,Django 创建应用的一个默认配置。
在这个阶段,您的代码不应该导入任何模型!
换句话说,你的应用程序的根包和定义应用配置类的模块不应该导入任何模型,即使是间接导入。
严格地讲,Django 允许应用配置加载后导入模型。 然而,为了避免INSTALLED_APPS
的顺序带来不必要的约束,强烈推荐在这一阶段不导入任何模型。
这一阶段完成后,操作应用配置的API 开始变得可用,例如get_app_config()
。
然后Django 试图导入每个应用的models
子模块,如果有的话。
你必须在应用的models.py
或models/__init__.py
中定义或导入所有模型。 否则,应用注册表在此时可能不会完全填充,这可能导致ORM 出现故障。
一旦完成该步骤, get_model()
之类的 model API 可以使用了.
最后,Django 运行每个应用程序配置的ready()
方法。
下面是一些在你初始化的时候可能经常碰到的问题:
AppRegistryNotReady
:当导入应用程序配置或模型模块触发依赖于应用程序注册表的代码时,会发生这种情况。
例如,ugettext()
使用应用注册表来查找应用中的翻译目录。 若要在导入时翻译,你需要ugettext_lazy()
。 (使用ugettext()
将是一个bug,因为翻译会发生在导入的时候,而不是取决于每个请求的语言。)
模型模块中在导入时使用ORM 执行数据库查询也会引发此异常。 ORM 直到所有的模型都可用才能正常运转。
如果在一个独立的 Python 脚本中你忘了调用django.setup()
,也会发生此异常。
导入错误: cannot import name ...
如果导入出现循环,则会发生这种情况。
要消除这种问题,应尽量减少模型模块之间的依赖项,并在导入时尽可能少做工作。 为了避免在导入时执行代码,你可以移动它到一个函数和缓存其结果。 当你第一次需要其结果时,将执行该代码。 这一概念被称为"惰性求值"。
django.contrib.admin
在安装的应用中自动发现admin
。 要阻止它,请更改你的INSTALLED_APPS
以包含 'django.contrib.admin.apps.SimpleAdminConfig'
而不是 'django.contrib.admin'
。
2017年9月6日