Возврат из функции при продолжении выполнения - PullRequest
1 голос
/ 22 сентября 2011

Я работаю над приложением 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 ]

2 голосов
/ 22 сентября 2011

Лучше всего в этом случае использовать сельдерей .

Это позволяет создавать задачи, которые будут выполняться в фоновом режиме, без блокировки текущего запроса.

В вашем случае вы можете .save (), создать задачу, которая обновляет поля, отправить ее в свою очередь сельдерея, а затем вернуть желаемый ответ пользователю.

1 голос
/ 22 сентября 2011

Я не знаю ваших требований, но если эта операция занимает недопустимое время при сохранении, но приемлемое при доступе, я бы рассмотрел обработку FileSize, Duration, VideoFrameRate и т. Д. Как загрузку с отложенным доступомсвойства модели, при условии, что более длительное время начальной загрузки является достойным компромиссом для более короткого времени сохранения.

Есть много способов сделать это: вы можете кэшировать частоту кадров, например, с помощью структура кэширования при первом обращении к ней.Если вы предпочитаете сделать что-то, хранящееся в базе данных, вы можете получить доступ к частоте кадров с помощью свойства и вычислить его (и другие значения, если это необходимо), при первом обращении к нему, а затем сохранить их вбаза данных.Теоретически, это атрибуты самого файла, и поэтому ваш интерфейс не должен позволять их изменять и, следовательно, делать синхронизированными с файлом, на который они ссылаются.В этом направлении я мог бы сделать что-то вроде этого:

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, поэтому шаблон не нужно повторятьза каждого.

0 голосов
/ 22 сентября 2011

Похоже, что вы действительно хотите сделать, это вернуть объект, представляющий работу, которую еще нужно сделать, а затем присоединить обработчик завершения или наблюдателя к этому возвращенному объекту, который заполняет объект модели результатами, а затем вызывает super.save ().

Предостережение заключается в том, что я не уверен, насколько хорошо этот подход вписывается в модель приложения Django.

0 голосов
/ 22 сентября 2011

Я не знаю, будет ли ваш конкретный случай работать так, но я бы, вероятно, создал новую ветку, указывающую на ваш 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 не блокирует какие-либо данные, которые могут понадобиться в другом месте во время выполнения.

...