Ошибка "Файл не может быть открыт повторно." от django-imagekit после перемещения файла

Я использую django-imagekit для генерации миниатюры на модели Django:

class Book(models.Model):
    title = models.CharField(null=False, blank=False, max_length=255)
    thumbnail = models.ImageField(
        upload_to=upload_path, null=False, blank=True, default=""
    )

    list_thumbnail = ImageSpecField(processors=[ResizeToFit(80, 160)],
                                    source="thumbnail",
                                    format="JPEG")

Это работает нормально. Однако я пытаюсь переместить исходный файл thumbnail после загрузки. Вот упрощенная версия моего метода save(), которая просто перемещает файл в "новый" каталог и пересохраняет объект ( на самом деле все гораздо сложнее):

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

        if self.thumbnail and "/new/" not in self.thumbnail.path:
            # Move the thumbnail to correct location.
            initial_name = self.thumbnail.name
            initial_path = self.thumbnail.path
            new_name = os.path.join(os.path.dirname(initial_name),
                                    "new",
                                    os.path.basename(initial_name))
            new_path = os.path.join(settings.MEDIA_ROOT, new_name)

            if not os.path.exists(os.path.dirname(new_path)):
                os.makedirs(os.path.dirname(new_path))

            os.rename(initial_path, new_path)

            self.thumbnail.name = new_name
            kwargs["force_insert"] = False
            super().save(*args, **kwargs)

По умолчанию это работает нормально.

Но если у меня есть это в settings.py:

IMAGEKIT_DEFAULT_CACHEFILE_STRATEGY = "imagekit.cachefiles.strategies.Optimistic"

затем я получаю ошибки, вызванные сигналами imagekit, предположительно сбитого с толку тем, что файл переместился при попытке сгенерировать list_thumbnail. Вот часть трассировки:

...
  File "/venv-path/python3.8/site-packages/imagekit/specs/sourcegroups.py", line 33, in receiver
    fn(self, sender=sender, **kwargs)
  File "/venv-path/python3.8/site-packages/imagekit/specs/sourcegroups.py", line 101, in post_save_receiver
    self.dispatch_signal(source_saved, file, sender, instance,
  File "/venv-path/python3.8/site-packages/imagekit/specs/sourcegroups.py", line 124, in dispatch_signal
    signal.send(sender=source_group, source=file)
  File "/venv-path/python3.8/site-packages/django/dispatch/dispatcher.py", line 180, in send
    return [
  File "/venv-path/python3.8/site-packages/django/dispatch/dispatcher.py", line 181, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
  File "/venv-path/python3.8/site-packages/imagekit/registry.py", line 116, in source_group_receiver
    call_strategy_method(file, callback_name)
  File "/venv-path/python3.8/site-packages/imagekit/utils.py", line 166, in call_strategy_method
    fn(file)
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/strategies.py", line 30, in on_source_saved
    file.generate()
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/__init__.py", line 94, in generate
    self.cachefile_backend.generate(self, force)
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/backends.py", line 109, in generate
    self.generate_now(file, force=force)
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/backends.py", line 96, in generate_now
    file._generate()
  File "/venv-path/python3.8/site-packages/imagekit/cachefiles/__init__.py", line 98, in _generate
    content = generate(self.generator)
  File "/venv-path/python3.8/site-packages/imagekit/utils.py", line 152, in generate
    content = generator.generate()
  File "/venv-path/python3.8/site-packages/imagekit/specs/__init__.py", line 153, in generate
    self.source.open()
  File "/venv-path/python3.8/site-packages/django/db/models/fields/files.py", line 77, in open
    self.file.open(mode)
  File "/venv-path/python3.8/site-packages/django/core/files/base.py", line 114, in open
    raise ValueError("The file cannot be reopened.")
ValueError: The file cannot be reopened.

Я мог бы изменить стратегию кэшфайла, но я хотел бы сохранить ее. Поэтому я не уверен, как сообщить полям imagekit о новом местоположении файла.

Оказалось, что помимо обновления атрибута ImageField объекта name, мне пришлось обновить и его File объект path.

Поэтому я добавил следующее:

            self.thumbnail.file.path = new_path

к методу сохранения:

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

        if self.thumbnail and "/new/" not in self.thumbnail.path:
            # Move the thumbnail to correct location.
            initial_name = self.thumbnail.name
            initial_path = self.thumbnail.path
            new_name = os.path.join(os.path.dirname(initial_name),
                                    "new",
                                    os.path.basename(initial_name))
            new_path = os.path.join(settings.MEDIA_ROOT, new_name)

            if not os.path.exists(os.path.dirname(new_path)):
                os.makedirs(os.path.dirname(new_path))

            os.rename(initial_path, new_path)

            self.thumbnail.name = new_name
            self.thumbnail.file.path = new_path  # THE NEW LINE

            kwargs["force_insert"] = False
            super().save(*args, **kwargs)
Вернуться на верх