Как запланировать задание с Celery, которое запускается через три месяца и только один раз - PullRequest
0 голосов
/ 09 февраля 2020

Я создал рекламное приложение в своем веб-сайте с django, и модель объявления выглядит следующим образом

AdModel(models.Model):

    starting_date = models.DateField()
    expiring_date = models.DateField()
    active = models.BooleanField(default=False)

Моя цель - создать задачу, используя Celery для активации (set ad.active = True ) и деактивировать объявления в зависимости от даты их начала и истечения срока действия, а также потому, что ритм сельдерея предназначен для периодической c повторяющейся задачи , поэтому ее не удастся решить.

Возможно, пройдя исходная_дата как решение задачи, подобной этой, будет решением:

#signals.py
@receiver(post_save, sender=AdModel)
def start_task(sender, instance, created, **kwargs):
    if created:
        ad_activator_task.apply_async( (instance.id), eta=instance.starting_date)

Если instance.starting_date еще через три месяца, будет ли оно выполнено? я читал, что Celery не годится для долгосрочных задач в будущем (далекое будущее) , и я немного растерялся.

примечание: я использую Redis в качестве брокера

Ответы [ 2 ]

0 голосов
/ 10 февраля 2020

Я нашел неуместный обходной путь, когда w sh создает периодическую задачу c, которая запускается каждый день в полночь для проверки наличия / отключения рекламы, и задача выглядит так:

#tasks.py
@shared_task
def activate_deactivate_ads():
    to_activate = AdModel.objects.filter(date_started=timezone.now().date())
    if to_activate.exists():
        to_activate.update(active=True)

    to_deactivate = AdModel.objects.filter(date_expires=timezone.now().date(), active=True)
    if to_deactivate.exists():
        to_deactivate.update(active=False)

и конфигурация ритма:

#settings.py
CELERY_BEAT_SCHEDULE = {
'activate-deactivate-ads': {
   'task': 'tasks.activate_deactivate_ads',
    # Excecute everyday at midnight
   'schedule': crontab(minute=0, hour=0),
    }
  }

приветствуются любые другие подходы

0 голосов
/ 09 февраля 2020

Я настоятельно советую , а не , чтобы реализовать это с помощью запланированных задач. В любом случае, лучше не использовать поле active: здесь активна реклама, зависит от starting_date и expiring_date. Таким образом, мы можем смоделировать это как:

class AdModel(models.Model):
    starting_date = models.DateField()
    expiring_date = models.DateField()

Мы можем использовать аннотации, чтобы определить, активен ли AdModel с:

from django.db.models import BooleanField, ExpressionWrapper, Q
from django.db.models.functions import Now

AdModel.objects.annotate(
    active=ExpressionWrapper(
        Q(starting_date__lte=Now(), expring_date__gte=Now()),
        output_field=BooleanField()
    )
)

Если вам это нужно часто, вы можете сделать Manager, который будет автоматически аннотировать AdModel.objects:

class <b>AdModelManager</b>(models.Manager):

    def get_queryset(self):
        return super().get_queryset().annotate(
            active=ExpressionWrapper(
                Q(starting_date__lte=Now(), expring_date__gte=Now()),
                output_field=BooleanField()
            )
        )

class AdModel(models.Model):
    starting_date = models.DateField()
    expiring_date = models.DateField()

    <b>objects = AdModelManager()</b>

Если вам нужен дополнительный контроль, вы можете просто добавить дополнительное логическое поле Nullable force_active, которое по умолчанию установлено в NULL, но вы можете установить True / False, чтобы переопределить механизм истечения:

from django.db.models import Coalesce

class AdModelManager(models.Manager):

    def get_queryset(self):
        return super().get_queryset().annotate(
            active=<b>Coalesce('force_activate',</b> ExpressionWrapper(
                Q(starting_date__lte=Now(), expring_date__gte=Now()),
                output_field=BooleanField()
            )<b>)</b>
        )

class AdModel(models.Model):
    starting_date = models.DateField()
    expiring_date = models.DateField()
    <b>force_activate</b> = models.BooleanField(null=True)

    objects = AdModelManager()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...