应用¶ T0>

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.pyurls.pywsgi.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.adminAppConfig中的功能,而不需要用户更新他们的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 子模块定义特定于项目的配置类是一项惯例,并不要求。

应用的配置

class AppConfig[source]

AppConfig的对象存储应用的元数据。 某些属性可以在AppConfig的子类中配置。 其它的配置由Django设置且是只读的。

可配置属性

AppConfig.name

应用的完整Python路径,例如'django.contrib.admin'

这个属性定义配置适用于哪个应用。 它必须在所有 AppConfig 子类中设置。

它在整个Django 项目中必须是唯一的。

AppConfig.label

应用的缩写名称,例如'admin'

此属性允许重新标记应用,当两个应用程序有冲突的标签。 它默认为name 的最后一部分。 它应该是一个有效的 Python 标识符。

它在整个Django 项目中必须是唯一的。

AppConfig.verbose_name

应用的适合阅读的名称,例如“Administration”。

此属性默认为label.title()

AppConfig.path

应用目录的文件系统路径,如 '/usr/lib/python3.4/dist-packages/django/contrib/admin'

在大多数情况下,Django 可以自动检测并此设置,但你也可以在AppConfig 子类上提供一个显式的类属性以覆盖它 。 在一些情况下,这是必需的;例如,如果应用的Python包是具有多个路径的命名空间程序包

只读属性

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_migratepost_migrate 只有在应用具有models 模块时才发出。

方法¶ T0>

AppConfig.get_models()[source]

为此应用程序返回一个Model类的迭代。

需要应用程序注册表完全填充。

AppConfig.get_model(model_name, require_ready=True)[source]

返回具有给定 model_nameModel model_name 是不区分大小写。

如果此应用程序中没有此类模型,则引发LookupError

需要完全填充应用注册表,除非require_ready参数设置为False require_ready的行为与apps.get_model()完全相同。

Django中的新功能1.11:

添加了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 +)

Python版本3.3和更高版本支持不包含__init__.py文件的Python包。 这些包称为“命名空间包”,可以分布在sys.path(请参阅 PEP 420)上的不同位置的多个目录中。

Django应用需要文件系统的一个基本路径,其中Django(根据配置)将搜索模板、静态文件等。 因此,只有满足以下条件之一,命名空间包才能是Django应用:

  1. 命名空间包实际上只有一个位置(即不会分布在多个目录中)。
  2. 用于配置应用的AppConfig类具有path属性,它是一个绝对路径,Django将用它作为应用的基本路径。

如果这些条件都不满足,Django将产生ImproperlyConfigured

应用程序注册表

apps

应用的注册表提供下列公共API。 没有在下面列出的方法被认为是私有的,恕不另行通知。

apps.ready

注册表完全填充后设置为True的布尔属性,并调用所有AppConfig.ready()方法。

apps.get_app_configs()

返回一个由AppConfig 实例组成可迭代对象。

apps.get_app_config(app_label)

返回具有给定app_labelAppConfig 如果没有这种模型存在,抛出LookupError

apps.is_installed(app_name)

检查注册表中是否存在具有给定名称的应用。 app_name 是应用的完整名称,例如'django.contrib.admin'

apps.get_model(app_label, model_name, require_ready=True)

返回具有给定app_labelmodel_nameModel 作为快捷方式,此方法还接受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_readyFalse时,get_model()返回可能不完全正常运行的模型类(反向访问者可能会丢失),直到应用程序注册表已完全填充。 因此,最好将require_ready保留到默认值True

Django中的新功能1.11:

添加了require_ready关键字参数。

初始化过程

应用如何加载

当Django 启动时,django.setup() 负责填充应用注册表。

setup(set_prefix=True)[source]

配置Django:

  • 加载设置。
  • 设置日志。
  • 如果set_prefix为True,则将URL解析器脚本前缀设置为FORCE_SCRIPT_NAME,否则为/
  • 初始化应用注册表。
在Django更改1.10:

设置URL解析器脚本前缀的功能是新的。

自动调用此函数︰

  • 当运行一个通过Django 的WSGI 支持 的HTTP 服务器。
  • 当调用管理命令。

在其他情况下它必须显式调用,例如在普通的 Python 脚本中。

应用注册表初始化分三个阶段。 在每个阶段,Django 以INSTALLED_APPS 中的顺序处理所有应用。

  1. 首先,Django 会导入INSTALLED_APPS中的所有应用。

    如果它是一个应用配置类,Django 导入应用的根包,通过其name 属性。 如果它是一个Python 包,Django 创建应用的一个默认配置。

    在这个阶段,您的代码不应该导入任何模型!

    换句话说,你的应用程序的根包和定义应用配置类的模块不应该导入任何模型,即使是间接导入。

    严格地讲,Django 允许应用配置加载后导入模型。 然而,为了避免INSTALLED_APPS 的顺序带来不必要的约束,强烈推荐在这一阶段不导入任何模型。

    这一阶段完成后,操作应用配置的API 开始变得可用,例如get_app_config()

  2. 然后Django 试图导入每个应用的models 子模块,如果有的话。

    你必须在应用的models.pymodels/__init__.py 中定义或导入所有模型。 否则,应用注册表在此时可能不会完全填充,这可能导致ORM 出现故障。

    一旦完成该步骤, get_model() 之类的 model API 可以使用了.

  3. 最后,Django 运行每个应用程序配置的ready() 方法。

Troubleshooting

下面是一些在你初始化的时候可能经常碰到的问题:

  • 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'