skip to Main Content

I have created an advertising app in my websilte with django and the Ad model looks like this

AdModel(models.Model):

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

My goal is to create a task using Celery to activate (set ad.active = True) and deactivate ads based on their starting and expiring date and since the celery beat is for periodic recurring task so it’s not going to solve it.

Probably passing the starting_date as eta to the task like this will be the solution:

#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)

If the instance.starting_date is three months far from now, is it going to be executed ? i have read that Celery not usefull for long term future tasks (far future) and i’m kinda confused.

note: im using Redis as broker

2

Answers


  1. Chosen as BEST ANSWER

    I have come up with inelegant workaround wish is creating a periodic task that runs everyday at midnight to check for ads to activate/deactivate and the task is like so:

    #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)
    

    and the beat config :

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

    any other approachs are welcome


  2. I strongly advise not to implement this with scheduled tasks. You better do not use an active field anyway: whether an ad is active is here determine by the starting_date and the expiring_date. We can thus model this as:

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

    We can use annotations to determine if an AdModel is active with:

    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()
        )
    )

    If you need this often, you can make a Manager that will automatically annotate AdModel.objects:

    class AdModelManager(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()
    
        objects = AdModelManager()

    If you want extra control, you can just add an extra nullable boolean field force_active that is set to NULL by default, but you can set it to True/False to override the expiration mechanism:

    from django.db.models import Coalesce
    
    class AdModelManager(models.Manager):
    
        def get_queryset(self):
            return super().get_queryset().annotate(
                active=Coalesce('force_activate', 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()
        force_activate = models.BooleanField(null=True)
    
        objects = AdModelManager()
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search