Django:如何在同一对象中提到的时间自动更改字段的值?



我正在为赛车事件开发django项目,其中数据库中的一个表有三个字段。

1) 布尔字段,用于了解种族是否处于活动状态

2) 比赛开始时间

3) 比赛结束时间

在创建它的对象时,会指定start_time和end_time。如何在比赛开始时将布尔字段的值更改为True,在比赛结束时将
更改为False?如何安排这些活动?

要在特定时间后自动更新模型字段,可以使用Celery任务

步骤1:创建芹菜任务

我们将首先创建一个名为set_race_as_inactive的芹菜任务,该任务将在当前日期大于race_objectend_time之后将race_objectis_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_objectend_time之后执行。

我们将使用apply_async()调用任务,并将eta参数作为race_objectend_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.pkNone的这种检查是这样做的,即只有在创建新对象的情况下,才会创建一个芹菜任务。如果我们不这样做,那么对于每一个.save()调用(INSERTUPDATE),都会创建一个我们不想要的芹菜任务。这将导致许多不必要的芹菜任务等待执行,并使我们的芹菜队列过载。

使用Celery的好处is_active标志的更新将在后台异步自动进行,而无需担心手动更新。每次创建新的竞赛对象时,都会触发一个任务,Celery会将其执行推迟到竞赛的end_timeend_time结束后,Celery将执行该任务。

假设以下场景-

  1. 您希望独立于数据库
  2. 一旦比赛结束,它就再也不会开始,所以一旦activefalse,它就永远不会是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)

相关内容

  • 没有找到相关文章

最新更新