Ошибка "Файл не может быть открыт повторно." от 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)