在继续执行时从函数返回

时间:2011-09-21 23:49:02

标签: python django nonblocking

我正在开发一个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)

我想要帮助的是如何在实际完成之前从函数返回。我想调用该函数,然后只要它被正确调用就不返回任何内容。但是该函数应该继续运行,然后在完成后更新对象。如果我需要澄清一些事情,请告诉我。还有,事情甚至有用吗?

由于

4 个答案:

答案 0 :(得分:2)

在这种情况下,最好的选择是使用celery

这使您可以创建将在后台发生的任务,而不会阻止当前请求。

在您的情况下,您可以.save(),创建更新字段的任务,将其推送到您的芹菜队列,然后将所需的响应返回给用户。

答案 1 :(得分:1)

我不知道您的要求,但是如果此操作在保存时花费了不可接受的时间,但在访问时是可接受的,我会考虑处理FileSizeDurationVideoFrameRate,等,作为模型的延迟加载属性,假设较长的初始加载时间是较短的保存时间的适当权衡。

您可以通过多种方式执行此操作:例如,您可以在第一次访问时使用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应用程序模型中的适用程度。