Django — Обход загрузки кода в функции apps.ready при выполнении миграции или миграции в базу данных

#python #django

Вопрос:

Для начала, я искал это раньше, но мне не повезло найти что-то похожее на это.

Фон:

Я создал приложение для инвентаризации для работы, чтобы помочь моей команде быстро просматривать статистику по нашей ИТ-инфраструктуре. У меня есть потоки, запускаемые при загрузке приложения, чтобы запустить некоторые функции очистки. Этот код отлично работает, но это происходит всякий раз, когда я создаю и применяю миграции баз данных (manage.py совершайте миграции amp; manage.py мигрировать).

Цель:

Я хотел бы запустить код очистки только тогда, когда я выполняю команду runserver (manage.py runserver). Таким образом, у меня нет ресурсов, конкурирующих между деятельностью по миграции и деятельностью по очистке. Это также часто приводит к большому количеству ошибок, потому что иногда не все модели/поля базы данных еще существуют в базе данных.

Идеи:

  1. Измените код в репозитории django, чтобы ввести флаг, который я затем смогу проверить, прежде чем запускать код очистки. Не рекомендуется, это будет перезаписано при обновлении django, и оно не будет сохраняться между моим сервером разработки и сервером prod.
  2. Найдите способ проверить, какая команда выполняется с помощью manage.py, и введите проверку, чтобы начать очистку только в том случае, если эта команда запущена. Рекомендуется, чтобы это оставалось в моей кодовой базе и могло легко перемещаться между экземплярами dev и prod.

Я открыт для других идей для достижения этой цели. Если есть другой способ начать очистку при запуске приложения, то это, скорее всего, тоже сработает. Функция apps.ready была единственным, что я смог найти, чтобы запустить что — то при запуске приложения.

Изменить: Вот что находится внутри apps.ready() функции:

     def ready(self):
        set_default_database_items()
        if environment == "prod":
            from .threading.scraping import TimerScrape
            from .threading.keep_alive import KeepAliveThread
            TimerScrape()
            KeepAliveThread(1)
            KeepAliveThread(2)
 

Вот посмотрите на TimerScrape() нить:

     def run(self):
        sleep(60)
        while True:
            idle = True
            vcenters = Vcenter.objects.all()
            connection.close()
            netapps = StorageSystem.objects.all()
            connection.close()
            rubriks = BackupSystem.objects.all()
            connection.close()
            current_time = datetime.now(timezone.utc)

            # get list of current threading and their names
            threads = enumerate()
            thread_list = []
            for thread in threads:
                thread_list.append(thread.name)

            # go through each vCenter and start scrape
            for vc in vcenters:
                thread_name = vc.name   "_thread"
                if thread_name not in thread_list:
                    if vc.last_updated is None:
                        self.vcscrape(vc.name, vc.user, vc.password)
                    elif vc.last_updated is not None:
                        time_difference = current_time - vc.last_updated
                        if time_difference.seconds > 14400:
                            self.vcscrape(vc.name, vc.user, vc.password)
                        else:
                            print("vCenters: Too soon to update vCenter "   vc.name)
                else:
                    idle = False
                    print("vCenter "   vc.name   " update is in progress")

            # go through each NetApp and start scrape
            for cluster in netapps:
                thread_name = cluster.name   "_thread"
                if thread_name not in thread_list:
                    if cluster.last_updated is None:
                        self.netappscrape(cluster.name, cluster.user, cluster.password)
                    elif cluster.last_updated is not None:
                        time_difference = current_time - cluster.last_updated
                        if time_difference.seconds > 14400:
                            self.netappscrape(cluster.name, cluster.user, cluster.password)
                        else:
                            print("Clusters: Too soon to update Cluster "   cluster.name)
                else:
                    idle = False
                    print("Cluster "   cluster.name   " update is in progress")

            # go through each Rubrik and start scrape
            for cluster in rubriks:
                thread_name = "backup_"   cluster.name   "_thread"
                if thread_name not in thread_list:
                    if cluster.last_updated is None:
                        self.rubrikscrape(cluster.name, cluster.user, cluster.password)
                    elif cluster.last_updated is not None:
                        time_difference = current_time - cluster.last_updated
                        if time_difference.seconds > 14400:
                            self.rubrikscrape(cluster.name, cluster.user, cluster.password)
                        else:
                            print("Backups: Too soon to update Cluster "   cluster.name)
                else:
                    idle = False
                    print("Backups "   cluster.name   " update is in progress")

            if idle:
                platforms = Platform.objects.all()
                connection.close()
                applications = Application.objects.all()
                connection.close()
                functions = Function.objects.all()
                connection.close()
                regions = Region.objects.all()
                connection.close()
                sites = Site.objects.all()
                connection.close()
                environments = Environment.objects.all()
                connection.close()
                tag_reports = TagsReport.objects.all()
                connection.close()

                for obj in platforms:
                    thread_name = "Tag_report_"   "platform_"   obj.name   "_thread"
                    if thread_name not in thread_list:
                        if obj.last_updated is None:
                            self.tagscrape(obj, "platform")
                        elif obj.last_updated is not None:
                            time_difference = current_time - obj.last_updated
                            if time_difference.seconds > 14400:
                                self.tagscrape(obj, "platform")
                            else:
                                print("Too soon to update platform "   obj.name)

                for obj in applications:
                    thread_name = "Tag_report"   "application_"   obj.name   "_thread"
                    if thread_name not in thread_list:
                        if obj.last_updated is None:
                            self.tagscrape(obj, "application")
                        elif obj.last_updated is not None:
                            time_difference = current_time - obj.last_updated
                            if time_difference.seconds > 14400:
                                self.tagscrape(obj, "application")
                            else:
                                print("Too soon to update application "   obj.name)

                for obj in functions:
                    thread_name = "Tag_report"   "function_"   obj.name   "_thread"
                    if thread_name not in thread_list:
                        if obj.last_updated is None:
                            self.tagscrape(obj, "function")
                        elif obj.last_updated is not None:
                            time_difference = current_time - obj.last_updated
                            if time_difference.seconds > 14400:
                                self.tagscrape(obj, "function")
                            else:
                                print("Too soon to update function "   obj.name)

                for obj in regions:
                    thread_name = "Tag_report"   "region_"   obj.name   "_thread"
                    if thread_name not in thread_list:
                        if obj.last_updated is None:
                            self.tagscrape(obj, "region")
                        elif obj.last_updated is not None:
                            time_difference = current_time - obj.last_updated
                            if time_difference.seconds > 14400:
                                self.tagscrape(obj, "region")
                            else:
                                print("Too soon to update region "   obj.name)

                for obj in sites:
                    thread_name = "Tag_report"   "site_"   obj.name   "_thread"
                    if thread_name not in thread_list:
                        if obj.last_updated is None:
                            self.tagscrape(obj, "site")
                        elif obj.last_updated is not None:
                            time_difference = current_time - obj.last_updated
                            if time_difference.seconds > 14400:
                                self.tagscrape(obj, "site")
                            else:
                                print("Too soon to update site "   obj.name)

                for obj in environments:
                    thread_name = "Tag_report"   "environment_"   obj.name   "_thread"
                    if thread_name not in thread_list:
                        if obj.last_updated is None:
                            self.tagscrape(obj, "environment")
                        elif obj.last_updated is not None:
                            time_difference = current_time - obj.last_updated
                            if time_difference.seconds > 14400:
                                self.tagscrape(obj, "environment")
                            else:
                                print("Too soon to update environment "   obj.name)

                for obj in tag_reports:
                    thread_name = "Missing_tags_report_thread"
                    if thread_name not in thread_list:
                        if obj.last_updated is None:
                            self.missing_tag_scrape(obj)
                        elif obj.last_updated is not None:
                            time_difference = current_time - obj.last_updated
                            if time_difference.seconds > 14400:
                                self.missing_tag_scrape(obj)
                            else:
                                print("Too soon to update missing tags reports")
            sleep(900)
 

Небольшое объяснение. Идея состоит в том, что каждые 15 минут этот поток будет проверять, прошло ли 4 часа с момента последнего зарегистрированного обновления для каждого из этих элементов. Если это так, и для объекта не выполняется поток очистки, он запустит новое задание очистки для обновления информации в базе данных.

Если никаких царапин не происходит, это позволит запустить некоторые отчеты, если с момента последнего запуска прошло 4 часа.

Этот поток является автономным, и, как я уже упоминал ниже в своем ответе, я смог выяснить, что если я установлю 60-секундный таймер сна в начале потока, я смогу избежать возникновения проблем при apps.ready() загрузке функции при запуске приложения.

Комментарии:

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

Ответ №1:

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

Кроме того, запуск кода очистки, когда вы это делаете ./manage.py runserver , звучит как плохая идея. Вы должны использовать только runserver для разработки, а не в качестве реальной веб-службы в производственной среде. Вместо этого вам следует развернуть с помощью реального веб — сервера, такого как Apache или Nginx.

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

Комментарии:

1. Код scaping должен запускаться каждый раз при запуске приложения. Вся идея заключается в том, что если с момента последнего обновления прошло 4 часа, он выходит и обновляет данные. Он построен на чистом python с использованием SDK, идея которого заключается в том, что единственное, что нужно сделать, это запустить приложение, а затем просто оставить его в покое. Я не пытаюсь обойти функцию apps.ready, но, учитывая, как django загружает платформу приложений при выполнении миграции, часть очистки не нужно запускать, так как она будет выполнять только действия с базой данных.

2. @JimmyFort Похоже, что вы используете Django непреднамеренным образом. Продолжение этого пути принесет только боль и страдания. Вы должны использовать его ТОЛЬКО runserver в целях разработки. Его НИКОГДА не следует использовать для запуска приложения. Для запуска кода очистки следует использовать cron или аналогичный планировщик. Джанго не предназначен для этой задачи.

3. @JimmyFort Я предлагаю вам узнать о том, как работает веб — приложение. Я объяснил некоторые из них здесь на очень высоком уровне: обычно вы должны развертывать приложение Django с использованием реального веб-сервера, такого как Apache или Nginx. Если вы не знакомы с ними, сейчас самое подходящее время для обучения. Вы быстро увидите, как вы пытаетесь засунуть круглый колышек в квадратное отверстие.

4. Джанго не выполняет работу хрона. Весь код очистки самодостаточен в своих собственных классах и потоках. Он содержит свои собственные функции планирования и cron. Вся идея заключается в том, что поток запускается при запуске приложения, а затем он запускается сам по себе без дополнительного ввода, необходимого с веб-сервера. Единственное, что достигается с помощью Django, — это просто запуск потока.

5. @Джимми, я думаю, что лучше понимаю, что ты имеешь в виду. Мне все еще кажется, что запуск потоков скребка apps.ready() -неправильное решение проблемы. И похоже, что cron-это тоже не совсем то, что вам нужно. Возможно, вам нужен внешний скрипт (bat, ps или sh в зависимости от платформы, на которой он работает), который запускает приложение django и службу скребка.

Ответ №2:

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

Я также рассматривал возможность использования Apache с mod_wsgi, однако экземпляры как dev, так и prod работают в Windows, и mod_wsgi испытывает трудности с запуском в Windows без каких-либо манипуляций. В конечном итоге я мог бы перенести это на хост unix, но это потребовало бы некоторых дополнительных усилий, которые я бы предпочел не делать в настоящее время.