如何在Django "完全加载" 后运行任意代码

22 投票
4 回答
12111 浏览
提问于 2025-04-16 14:27

我需要在我的Django环境“完全加载”后执行一些相对简单的任务。

具体来说,我需要做一些事情,比如用Signal.disconnect()来断开一些由第三方库默认设置的Django信号,然后连接我自己的信号。此外,我还需要做一些“猴子补丁”,为另一个库中的一些Django模型添加方便的功能。

我一直在我的Django应用的__init__.py文件中做这些事情,这样做对猴子补丁来说似乎没问题,但对断开信号就不行。问题似乎出在时机上——不知为何,第三方库总是在我尝试用Signal.disconnect()断开信号之后才调用它的Signal.connect()

所以有两个问题:

根据我的INSTALLED_APPS的顺序,我能保证我的应用的__init__.py模块加载的顺序吗?

有没有合适的地方可以放置那些需要在Django应用完全加载到内存后执行的逻辑?

4 个回答

5

我需要做一些“猴子补丁”的工作。我用的是从GitHub分支上下载的django 1.5。我不知道这样做是否正确,但对我来说是有效的。

我不能使用中间件,因为我还想让manage.py脚本也受到影响。

总之,这里有一个相对简单的补丁:

import django
from django.db.models.loading import AppCache

django_apps_loaded = django.dispatch.Signal()

def populate_with_signal(cls):
    ret = cls._populate_orig()
    if cls.app_cache_ready():
        if not hasattr(cls, '__signal_sent'):
            cls.__signal_sent = True
            django_apps_loaded.send(sender=None)
    return ret

if not hasattr(AppCache, '_populate_orig'):
    AppCache._populate_orig = AppCache._populate
    AppCache._populate = populate_with_signal

然后你可以像使用其他信号一样使用这个信号:

def django_apps_loaded_receiver(sender, *args, **kwargs):
    # put your code here.
django_apps_loaded.connect(django_apps_loaded_receiver)
7

我的问题其实是对这个问题的一个不太清晰的重复提问:Django 启动代码应该放在哪里。这个问题的答案是:

写一个中间件,在初始化的时候执行这个操作,然后在初始化中抛出 django.core.exceptions.MiddlewareNotUsed,Django 会在所有请求中把它移除...

可以查看 Django 的关于编写自定义中间件的文档

14

在Django 1.7版本中,应用程序可以实现一个叫做ready()的方法:https://docs.djangoproject.com/en/dev/ref/applications/#django.apps.AppConfig.ready

撰写回答