29👍
To automatically update a model field after a specific time, you can use Celery tasks.
Step-1: Create a Celery Task
We will first create a celery task called set_race_as_inactive
which will set the is_active
flag of the race_object
to False
after the current date is greater than the end_time
of the race_object
.
This task will be executed by Celery
only if the current time is greater than the race object’s end_time
.
@app.task
def set_race_as_inactive(race_object):
"""
This celery task sets the 'is_active' flag of the race object
to False in the database after the race end time has elapsed.
"""
race_object.is_active = False # set the race as not active
race_object.save() # save the race object
Step-2: Call this celery task using eta
argument
After creating the celery task set_race_as_inactive
, we need to call this celery task.
We will call this task whenever we save a new race_object
into our database. So, whenever a newrace_object
will be saved, a celery task will be fired which will execute only after the end_time
of the race_object
.
We will call the task using apply_async()
and pass the eta
argument as the end_time
of the race_object
.
As per Celery docs,
The ETA (estimated time of arrival) lets you set a specific date and
time that is the earliest time at which your task will be executed.The task is guaranteed to be executed at some time after the specified
date and time, but not necessarily at that exact time.
from my_app.tasks import set_race_as_inactive
class RaceModel(models.Model):
...
def save(self, *args, **kwargs):
..
create_task = False # variable to know if celery task is to be created
if self.pk is None: # Check if instance has 'pk' attribute set
# Celery Task is to created in case of 'INSERT'
create_task = True # set the variable
super(RaceModel, self).save(*args, **kwargs) # Call the Django's "real" save() method.
if create_task: # check if task is to be created
# pass the current instance as 'args' and call the task with 'eta' argument
# to execute after the race `end_time`
set_race_as_inactive.apply_async(args=[self], eta=self.end_time) # task will be executed after 'race_end_time'
This checking of self.pk
with None
is done so that only in case of new objects creation, a celery task is created. If we don’t do this, then for every .save()
call (either INSERT
or UPDATE
) a celery task will be created which we don’t want. This will lead to many unnecessary celery tasks waiting to be executed and will overload our celery queues.
The benefit of using Celery is that updation of the is_active
flag will occur automatically in the background asynchronously without you needing to worry about manually updating them. Every time a new race object is created, a task will be fired and Celery will defer its execution until the end_time
of the race. After the end_time
has elapsed, Celery will execute that task.
4👍
Assuming the following scenarios –
- You want to be Database independent
- Once a race ends it never start again, so once
active
isfalse
it will never betrue
again.
There are numerous ways you can set that true automatically depending on your need –
If you need only when using the object, you can use a property –
@property
def active(self):
return self.end_date > datetime.datetime.utcnow() //I used local time if you want
you can also put it in the init –
def __init__(self):
super().__init__()
self.active = self.end_date > datetime.datetime.utcnow()
But this does not give you the option to perform query, because value is calculated after object is loaded in memory.
If you want to perform query, so we need to update the value in the database and save it. Assuming that when the race ends, you update the date in the overridden save method –
def save(self, *args, **kwargs):
self.active = self.end_date > datetime.datetime.utcnow()
super().save()
So when you save the object after the race ends, it will update the flag.
But if it is not possible for you to update the race when it ends and you need them to be calculated automatically you could use a scheduler. Like Celery
as @rahul suggested to update periodically. But for this option, you have to accept the fact that, the active
flag will not be updated at the exact time of the game ends. It will depend on how frequently you run the scheduler.
- How to use Django variable in JavaScript file?
- Django 1.6 and django-registration: built-in authentication views not picked up
- Django: How to check if something is an email without a form
2👍
It sounds to me that your “active” field should be a method instead like this:
from django.utils import timezone
class Race(models.Model):
start = models.DateTimeField()
end = models.DateTimeField()
def active(self):
now = timezone.now()
if self.start < now and now < self.end:
return True
return False
If you are using Django 1.7+ or South with older versions, this is a trivial change and will normalise your database too, unless the “active” field was intentionally created.
- Why "class Meta" is necessary while creating a model form?
- Django: check for modeladmin for a given model
-1👍
Is there any reason you wouldn’t just calculate the boolean field in your business logic? i.e. when you receive a request related to that race, simply check the times and assess whether or not the race is active (for display, analysis), etc.
I’m assuming you won’t have high load (one reason for pre-processing) like this.
- Django admin.TabularInline auto insert three blank row
- Make Django return response as a "different filename"
-1👍
You can override the predefined save method like this:
def save(self, *args, **kwargs):
if start:
boolean_field = True
super(YouModel, self).save(*args, **kwargs)
else:
boolean_filed = False
super(YouModel, self).save(*args, **kwargs)
- Sometimes request.session.session_key is None
- TypeError: ‘DoesNotExist’ object is not callable
- Make Django test case database visible to Celery
- Django load local json file
- Django reverse error: NoReverseMatch