在启动django开发服务器后立即调用函数。(Django/Python)

1 人关注

我试图在命令 python manage.py runserver 之后调用函数,它可以是简单的打印。我读了一些东西,发现关于 def ready(self) 的话题相当有趣,比如这个 重写AppConfig.ready() or Django启动时只执行一次代码? 我按照说明做了,但对于django 2.1.3版本来说,它并不工作。一次是ready函数没有被调用,另一次是我得到一堆 AppRegistryNotReady exceptions 。我认为在服务器启动后调用函数是很常见的事情。它不应该带来这么多麻烦,但我还是没有找到合适的解决方案。

我尝试了另一种解决方案

我创建了像这样的中间件类。

import time
class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        while True:
            print("true")
            time.sleep(5)
    def __call__(self, request):
        response = self.get_response(request)
        return response

Now I get proper output like :

Performing system checks...
System check identified no issues (0 silenced).
January 03, 2019 - 14:10:55
Django version 2.1.3, using settings 'CMS.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

BUT no connectionenter image description here

5 个评论
你说得很清楚,但我只是想确定:你想让函数调用在服务器准备好接受请求时发生,而不是在它得到自己的调用后立即开始?
@CharlesLandau 是的,当服务器准备好了,我想调用函数
你必须明白,"服务器启动后 "是一个相当模糊的定义。如果你使用的是多进程设置--这是在生产中最常见的情况--你将会在每个进程中有一个 "服务器启动"。如果这些进程是由前端服务器管理的(这也是最常见的情况),那么前端可能会在任何时候启动新的django进程。我们先不谈在负载均衡器后面有两个或更多的前台服务器,这里的 "服务器启动 "的定义是什么?
@CharlesLandau 我更新了问题
@brunodesthuilliers 我更新了问题
python
django
function
startup
django-2.1
KyluAce
KyluAce
发布于 2019-01-02
3 个回答
alfandango
alfandango
发布于 2021-09-25
已采纳
0 人赞同

Edit:

我仍然不确定你想达到什么目的,有一些评论者强调了一些重要的关切。首先, AppRegistryNotReady: Apps aren't loaded yet 的错误确实令人沮丧,我很理解。我不确定你是如何组织你的项目的,所以不能提供一个确切的解决方案。这可能是你组织文件的方式以及你从哪里导入你的应用程序的AppConfig类。你是否把它或任何自定义函数或类放在你的应用程序包层 __init__.py ,以便你从那里导入?如果是这样的话,这似乎会让Django感到困惑,参见 https://stackoverflow.com/a/34136825/2275482

Secondly:

我试图在命令之后调用函数 python manage.py runserver

我认为我们正在努力理解你想要实现的目标,以及你是否将Django应用程序的启动时间与服务器的启动时间混为一谈。正如评论者所指出的,网络服务器是完全独立于Django的,它是一个独立的进程。

即使是与Django捆绑的测试服务器也是独立于你的Django应用程序的。它只是一个简单的开发服务器,为了方便而放在那里,但它不是必需的,也不应该在生产中使用。Django的构建符合 PEP 333 .AFAIK,它可以在任何符合PEP 333的WSGI网络服务器上工作,无论是Apache和mod_wsgi、gunicorn还是uWSGI等等。如果你想在你使用的服务器上执行代码,你需要查看该服务器的文档。

替换代码3】像 django-admin 只是一个帮助开发的工具,见 https://docs.djangoproject.com/en/2.1/ref/django-admin/ .当你调用 manage.py runserver 时,你是在调用一个实用程序,它加载你的应用程序并设置一个测试服务器。你可以在文件中看到执行的流程,它正在导入哪些实用程序等等。这个执行流程与使用生产服务器时的情况不一样。

据我所知,生产服务器会从根目录下的 wsgi.py 文件加载你的django应用。它甚至不会去看 manage.py

所以这又回到了你想在服务器启动后发布消息的问题上,在这种情况下,这与Django无关,还是在你的Django应用程序启动时发布?

如果你想在Django应用启动后打印东西,被启动并加载到服务器上,那么你有很多选择。如果你真的想,你可以编辑并添加到 manage.py 中,但除此之外我不会篡改Django的核心。在使用不同的生产服务器时,这将被完全忽略。

如果你想编辑无论使用何种服务器都会被加载的入口点,那么你可以编辑项目的 wsgi.py 模块或单个应用程序的AppConfig ready() 方法。

如果你想建立独立于Django的东西,并在后台持续运行,那么正如@bruno-desthuilliers所指出的,你需要建立一个独立的进程,要么作为一个cron job,要么使用celery,甚至是django-carrot(如果你想的话),尽管后者仍然与应用程序的启动有关,但可以作为一个独立的工作者工作(我想)。Django-carrot只有非常简单的功能,用于小型工作。

如果这没有任何帮助,我很抱歉,对于所提供的信息我只能做到最好。

当我想在我的应用程序AppConfig的 ready() 方法中运行一些东西时,我也遇到了类似的问题。对我来说,这是我的应用程序在 INSTALLED_APPS 设置变量中的安装方式。

通常情况下,你会像这样注册你的应用程序。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'my_app',

然而这将加载用于注册你的应用程序的基础AppConfig类。如果你定制并覆盖了应用程序的AppConfig,这样你就可以声明你自己的ready()方法,一旦应用程序被实例化就会执行,你需要在INSTALLED_APPS中直接引用被覆盖的AppConfig。

INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'my_app.apps.MyAPPConfig', # MyAppConfig is the config class that inherits from AppConfig

另外,如果你不希望在一个特定的应用程序通过它的AppConfig加载时这样做,所以不是通过ready()的方法,而是在项目加载到服务器时,你可以在应用程序加载后在项目中放置一个wsgi.py模块

application = get_wsgi_application()
print('Hello World')

希望这对您有所帮助

参看我对这个问题的评论--如果你正在运行一个多进程服务器怎么办?
问题是要在 manage.py runserver 加载完毕后运行一个函数,这是一个测试服务器,而不是用于生产。OP没有说清楚这个函数是只用于测试还是用于生产。我的理解是在应用程序启动之后,正如你所说的 server start 有些含糊不清。
首先,你的答案与我给出的主题中的答案相同。当我试着在 ready() 中使用简单的打印时,我在输出端得到2个打印,然后是 "执行系统检查...... "首先这应该是相反的方式,先是 "执行系统检查...... "然后是打印。总之,当我使用我的最终函数时,它将一直运行,我得到 AppRegistryNotReady: Apps aren't loaded yet
@KyluAce 我已经更新了我的答案,但很抱歉我没有完全理解你想要做什么。这是我能给出的最好的答案,如果没有什么帮助,请原谅。
Charles Landau
Charles Landau
发布于 2021-09-25
0 人赞同

你可以在 manage.py 的末尾添加函数调用,在你的某个支持文件中,如 routes.py ,等等。但是!你表示你想让这个函数在服务器准备好接收请求时被调用。

重写 AppConfig.ready() 是为了在你子类化 AppConfig 并将其添加到你的应用程序注册表时使用。从你的问题中不清楚你是否遵循了这一部分。你可以仔细检查一下你是否遵循了指示 here 以确保你已经正确地做到了这一点。

现在我很确定我的函数不能使用 AppConfig.ready() ,所以我试着在 manage.py 的末尾调用函数(我的项目中没有routes.py文件),但我得到 AppRegistryNotReady: Apps aren't loaded yet.
你不能使用AppConfig.ready(),你应该尝试使用SubclassOfAppConfig.ready()。
Pranaysharma
Pranaysharma
发布于 2021-09-25
0 人赞同

我的解决方案有点不同,我愿意接受大家的建议。 灵感来自于@alfandango的回答,即使用wsgi.py来一次性运行自定义代码,以及其他一些与当前问题类似的参考资料,即把代码放在urls.py中会执行一次代码,但只在任何URL的第一次点击发生时执行。 请注意,该解决方案只有在以下情况下才是有帮助的/建议的。

  • You want to run database commands/access database when system first runs
  • You have just one worker. The code will run for more than one worker also but it will be invoked once for each worker
  • You are okay to delay execution of your code by 1 second(or less) as you will see.
  • 解决方案。 第1步:在根urls.py中添加以下代码。

    #normal URL.py invocation
    urlpatterns = [
        path('admin/', admin.site.urls),
        url(r'^somepaths/', include((some_url, 'some_url'), namespace="some_urls")),
    #define or import your function
    def run_after_system_start():
    #now run the functions
    run_after_system_start()
    

    第二步:在wsgi.py中运行wget来调用url.py ##wsgi.py的常规代码

    from django.core.wsgi import get_wsgi_application
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'applications.settings')
    import os
    application = get_wsgi_application()
    #below code hits itself(http://0.0.0.0) at some url to just invoke url.py and run the code once. The option for Wget are to enable no output or error is printed. It retries only once with a timeout of 1 second. Also the HIT type is of HTTP POST
        os.system('wget -T 1 -q -o /dev/null -O "/dev/null" --tries=1 --post-data "" http://0.0.0.0/someurl')
    except Exception as e: