Подтвердить что ты не робот

Как избежать использования метода AppConfig.ready(), выполняющегося дважды в Django

Я хочу выполнить некоторый код при запуске сервера Django, но я хочу, чтобы он запускался только один раз. В настоящее время, когда я запускаю сервер, он выполняется дважды. Документация говорит, что это может произойти и:

вы должны поместить флаг на своих классах AppConfig, чтобы предотвратить повторный запуск код, который должен выполняться ровно один раз.

Любая идея, как этого достичь? Заявление о печати ниже выполняется еще дважды.

from django.apps import AppConfig
import app.mqtt
from apscheduler.schedulers.background import BackgroundScheduler

class MyAppConfig(AppConfig):
    name = 'app'
    verbose_name = "HomeIoT"
    run_already = False

    def ready(self):
        if MyAppConfig.run_already: return
        MyAppConfig.run_already = True
        print("Hello")
4b9b3361

Ответ 1

Когда вы используете python manage.py runningerver Django запускает два процесса: один для реального сервера разработки и другой, чтобы перезагрузить приложение при изменении кода.

Вы также можете запустить сервер без опции перезагрузки, и вы увидите, что только один выполняемый процесс будет выполнен только один раз:

python manage.py runserver --noreload

Вы можете видеть эти ссылки, разрешить этот метод ready(), запущенный дважды в Django.

Ответ 2

Как сказал Роберто, вам нужно будет реализовать блокировку, чтобы сделать это, когда вы запускаете свой сервер с помощью команды runerver, если вы хотите использовать функциональные возможности auto_reload по умолчанию.

Django реализует его auto_reload через потоки и, таким образом, импортирует AppConfig в два отдельных потока, основной поток "команда/часы" и поток "перезагрузки", работающий на сервере. Добавьте в модуль инструкцию печати, и вы увидите это в действии. "Основной" поток загружает файлы AppConfig как часть его выполнения BaseCommand, а поток "reload" затем загружает их снова во время запуска сервера.

Если у вас есть код, который не может быть запущен в обоих этих потоках, ваши варианты несколько ограничены. Вы можете реализовать блокировку потока, чтобы поток "перезагрузить" не запускался(); вы можете перейти к рабочей среде для запуска вашего сервера (например, Gunicorn очень быстро настраивается, даже для тестирования); или вы можете вызвать свой метод другим способом, а не использовать ready().

Я бы посоветовал переехать в надлежащую среду, но лучший вариант действительно зависит от того, что именно должен делать тот метод, который вы вызываете.

Ответ 3

Вам необходимо реализовать блокировку. Это не простая проблема, и решение не будет казаться естественным, поскольку вы имеете дело с процессами и потоками. Имейте в виду, что есть много ответов на проблему блокировки, некоторые более простые подходы:

Блокировка файла: убедитесь, что в Linux существует один экземпляр приложения (обратите внимание, что потоки совместно используют блокировку файлов по умолчанию, поэтому этот ответ необходимо расширить для учета потоков).

Существует также ответ, который использует пакет Python, называемый tendo который инкапсулирует реализацию блокировки файла: fooobar.com/questions/66497/...

Сам Django предоставляет абстрактную портативную утилиту блокировки файлов в django.core.files.locks.

Ответ 4

если вы не хотите использовать --noreload вы можете:

замените строку в вашем приложении __init__.py которую вы используете для указания конфигурации:

default_app_config = 'mydjangoapp.apps.MydjangoappConfig'

этим:

import os

if os.environ.get('RUN_MAIN', None) != 'true':
    default_app_config = 'mydjangoapp.apps.MydjangoappConfig'

Ответ 5

Попробуйте использовать переменную экземпляра, а не переменную класса. Например:

class MyAppConfig(AppConfig):
    run_already = False

    def ready(self):
        if not self.run_already:
            print('Hello, world!')
            self.run_already = True