我正在开发一个Django应用程序,我想在首次创建对象时填充模型中的几个字段。目前我可以在我的模型的save()
例程中这样做:
def save(self, *args, **kwargs):
file = fileinfo.getfileinfo(self.file_path)
if not self.file_size:
self.file_size = file.FileSize
if not self.file_inode:
self.file_inode = file.FileInode
if not self.duration:
self.duration = file.Duration
if not self.frame_width:
self.frame_width = file.ImageWidth
if not self.frame_height:
self.frame_height = file.ImageHeight
if not self.frame_rate:
self.frame_rate = file.VideoFrameRate
super(SourceVideo, self).save(*args, **kwargs)
我在名为getfileinfo
的单独模块中创建了一个名为fileinfo
的函数。这是我的功能的一部分:
def getfileinfo(source):
fstats = os.stat(source)
info = dict({
u'FileSize': fstats.st_size,
u'FileInode': fstats.st_ino
})
output = subprocess.Popen(
[exiftool, '-json', source], stdout=subprocess.PIPE)
info.update(
json.loads(output.communicate()[0], parse_float=decimal.Decimal)[0])
return DotDict(info)
尽管所有这些都有效,但如果由于某种原因延迟检索过程,我想避免阻止保存过程。在对象创建时不需要该信息,并且可以在此后不久填充该信息。我的想法是,我会改变我的功能,接受有问题的文件路径以及作为对象的主键。有了这些信息,我可以获取信息,然后将我的对象条目更新为单独的操作。
类似的东西:
def save(self, *args, **kwargs):
fileinfo.getfileinfo(self.file_path, self.id)
super(SourceVideo, self).save(*args, **kwargs)
我想要帮助的是如何在实际完成之前从函数返回。我想调用该函数,然后只要它被正确调用就不返回任何内容。但是该函数应该继续运行,然后在完成后更新对象。如果我需要澄清一些事情,请告诉我。还有,事情甚至有用吗?
由于
答案 0 :(得分:2)
在这种情况下,最好的选择是使用celery。
这使您可以创建将在后台发生的任务,而不会阻止当前请求。
在您的情况下,您可以.save(),创建更新字段的任务,将其推送到您的芹菜队列,然后将所需的响应返回给用户。
答案 1 :(得分:1)
我不知道您的要求,但是如果此操作在保存时花费了不可接受的时间,但在访问时是可接受的,我会考虑处理FileSize
,Duration
,VideoFrameRate
,等,作为模型的延迟加载属性,假设较长的初始加载时间是较短的保存时间的适当权衡。
您可以通过多种方式执行此操作:例如,您可以在第一次访问时使用the caching framework缓存帧速率。如果您希望将其存储在数据库中,则可以通过property访问帧速率,并在第一次访问时计算它(以及其他值,如果适用),然后将它们存储在数据库中。从理论上讲,这些是文件本身的属性,因此您的界面不应允许更改它们,因此与它们引用的文件不同步。沿着这些方向,我可能会做这样的事情:
class MyMediaFile(models.Model):
file = models.FileField()
_file_size = models.IntegerField(null=True, editable=False)
_duration = models.IntegerField(null=True, editable=False)
<... etc ...>
@property
def file_size(self):
if self._file_size:
return self._file_size
else:
self.populate_file_info(self)
return self._file_size
def populate_file_info(self):
< ... do your thing here ... >
self._file_size = my_calcuated_file_size
< ... etc ... >
每个property
的逻辑可以很容易地分成一般的延迟加载@property
,因此不需要为每一个重复使用样板。
答案 2 :(得分:0)
我不知道你的具体情况是否会像这样,但我可能会做的是产生一个指向你super.save
的新线程,如下所示:
import threading
#other code here
def save(self, *args, **kwargs):
fileinfo.getfileinfo(self.file_path, self.id)
my_thread = threading.Thread(target=super(SourceVideo, self).save,
args=args, kwargs=kwargs)
my_thread.start()
这种方式save
将在后台运行,而其余代码将执行。
但是,如果save
没有阻止执行过程中其他地方可能需要的任何数据,那么这只会起作用。
答案 3 :(得分:0)
你真正想要做的是返回一个代表仍然需要完成的工作的对象,然后将一个完成处理程序或观察者附加到返回的对象,该对象用结果填充模型对象,然后调用super.save()。
需要注意的是,我不确定这种方法在Django应用程序模型中的适用程度。