我正在为赛车事件开发django项目,其中数据库中的一个表有三个字段。
1) 布尔字段,用于了解种族是否处于活动状态
2) 比赛开始时间
3) 比赛结束时间
在创建它的对象时,会指定start_time和end_time。如何在比赛开始时将布尔字段的值更改为True,在比赛结束时将
更改为False?如何安排这些活动?
要在特定时间后自动更新模型字段,可以使用Celery任务
步骤1:创建芹菜任务
我们将首先创建一个名为set_race_as_inactive
的芹菜任务,该任务将在当前日期大于race_object
的end_time
之后将race_object
的is_active
标志设置为False
。
只有当当前时间大于竞赛对象的end_time
时,Celery
才会执行此任务。
@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
步骤2:使用eta
参数调用此芹菜任务
在创建了芹菜任务set_race_as_inactive
之后,我们需要调用这个芹菜任务。
每当我们将新的race_object
保存到数据库中时,我们都会调用此任务。因此,每当保存新的race_object
时,就会触发一个芹菜任务,该任务将仅在race_object
的end_time
之后执行。
我们将使用apply_async()
调用任务,并将eta
参数作为race_object
的end_time
传递。
根据Celery文档,
ETA(预计到达时间)允许您设置特定日期和时间,即执行任务的最早时间。
任务保证在指定时间之后的某个时间执行日期和时间,但不一定在那个确切的时间。
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'
self.pk
与None
的这种检查是这样做的,即只有在创建新对象的情况下,才会创建一个芹菜任务。如果我们不这样做,那么对于每一个.save()
调用(INSERT
或UPDATE
),都会创建一个我们不想要的芹菜任务。这将导致许多不必要的芹菜任务等待执行,并使我们的芹菜队列过载。
使用Celery的好处是is_active
标志的更新将在后台异步自动进行,而无需担心手动更新。每次创建新的竞赛对象时,都会触发一个任务,Celery会将其执行推迟到竞赛的end_time
。end_time
结束后,Celery将执行该任务。
假设以下场景-
- 您希望独立于数据库
- 一旦比赛结束,它就再也不会开始,所以一旦
active
是false
,它就永远不会是true
有很多方法可以根据您的需要自动设置为true-
如果你只需要在使用对象时,你可以使用属性-
@property
def active(self):
return self.end_date > datetime.datetime.utcnow() //I used local time if you want
你也可以把它放在init-中
def __init__(self):
super().__init__()
self.active = self.end_date > datetime.datetime.utcnow()
但这并不能为您提供执行查询的选项,因为值是在对象加载到内存后计算的。
如果你想执行查询,那么我们需要更新数据库中的值并保存它。假设比赛结束时,你在重写的保存方法-中更新日期
def save(self, *args, **kwargs):
self.active = self.end_date > datetime.datetime.utcnow()
super().save()
因此,当您在比赛结束后保存对象时,它将更新标志。
但是,如果你不可能在比赛结束时更新比赛,并且你需要自动计算,你可以使用调度器。就像@rahul建议定期更新的Celery
一样。但对于这个选项,您必须接受这样一个事实,即active
标志不会在游戏结束的确切时间更新。这将取决于您运行调度程序的频率。
在我看来,您的"活动"字段应该是这样的方法:
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
如果您使用Django 1.7+或South的旧版本,这是一个微不足道的更改,也会使您的数据库正常化,除非有意创建"活动"字段。
您有没有理由不计算业务逻辑中的布尔字段?即,当您收到与该比赛相关的请求时,只需检查时间并评估比赛是否处于活动状态(用于显示、分析)等。
我假设您不会有像这样的高负载(预处理的一个原因)。
您可以覆盖预定义的保存方法,如下所示:
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)