在Django中装饰模型类并在装饰器中调用保存
我有几个模型,它们会把日志数据保存到我的数据库里。我还做了一个“最近事件”的应用程序,我想选择哪些模型可以把数据发送到这个事件应用。我觉得用装饰器来实现这个功能挺好的,这样我只需要把装饰器加到我想要的模型上就行了:
@logger
class TemperatureLog(models.Model):
Date = models.DateTimeField(auto_now_add=True)
Device = models.ForeignKey(TemperatureDevice)
Data = models.PositiveIntegerField()
这是事件模型,我使用了通用外键:
class Event(models.Model):
Active = models.BooleanField()
Queue = models.BooleanField()
ContentType = models.ForeignKey(ContentType)
ObjectID = models.PositiveIntegerField()
Event = generic.GenericForeignKey('ContentType', 'ObjectID')
这是装饰器:
def logger(event):
def wrap(*args, **kwargs):
from toolbox.event.models import Event
event(*args, **kwargs).save()
myid = event(*args, **kwargs).id
new = Event(Event=event.objects.get(id=myid))
if Event.objects.all().filter(Active=True).count() >= 25:
new.Queue = True
new.save()
else:
new.Active = True
new.save()
for item in Event.objects.all().filter(Queue=True):
item.Queue = False
item.Active = True
item.save()
if Event.objects.all().filter(Active=True).count() >= 25:
break
return event(*args, **kwargs)
return wrap
这个装饰器工作得很好,它能创建事件实例并保存。但是我遇到的问题是,save() 方法会被调用两次。一次是在装饰器里,另一次是在实际收集温度日志的代码中(因为我无法提前知道哪些应用会发送事件,哪些不会,或者将来可能会改变)。所以我在想有没有更优雅的方法来解决这个问题。我喜欢用装饰器的方式,因为我只需要把它加到模型类上,但我对 save 被调用两次这件事并不是很放心。
2 个回答
简单来说,针对你的问题,可以考虑使用Django内置的pre_save
信号。
基本上,你需要把一个监听函数连接到pre_save
信号上,具体的使用方法可以在上面的链接中找到。这样,你就可以在保存模型实例之前修改它的某些属性。只有在你的监听函数执行完毕后(还有其他连接到这个模型的pre_save
信号的监听函数也执行完),模型实例才会被保存到数据库中。
如果我理解你的代码没错,你希望当数据库中有25个或更多的活动事件记录时,Queue
变量设置为True
,否则设置为False
(而Active
的情况正好相反——我不太明白你为什么需要两个布尔值)。你可以通过信号来实现这个功能,像这样……
from django.db.signals import pre_save
def update_event_active_queue_status(sender, instance=None, **kwargs):
if Event.objects.filter(Active=True).count() >= 25:
instance.Queue = True
else:
instance.Active = True
pre_save.connect(update_event_active_queue_status, sender=Event)
你还想解决的另一个问题是,当活动事件的数量降到25以下时,把排队的事件重新变为活动状态。我不太清楚你的具体需求,但我觉得这可能不适合在这里讨论,可能更适合用定时任务(cron job)或者其他事件管理工具来处理。现在,如果系统中没有添加新事件(或者以其他方式进行更改),队列中的项目就永远不会被取出。这可能不是你想要的结果。
当然,你对自己的需求比我更了解,所以我的建议仅供参考。
你觉得对所有模型使用 post_save 信号怎么样?
def log_saved_event(sender, instance, signal, *args, **kwargs):
# handle Event class
pass
from django.db.models import signals
from django.db import models
for m in models.get_models():
signals.post_save.connect(log_saved_event, sender=m)