I'm using django-imagekit to generate a thumbnail on a Django model:
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")
That works fine. However I'm trying to move the original thumbnail
file after upload. Here's a simplified version of my save()
method, that just moves the file into a "new" directory and re-saves the object (it's more complicated than that really):
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)
This works fine by default.
But if I have this in settings.py
:
IMAGEKIT_DEFAULT_CACHEFILE_STRATEGY = "imagekit.cachefiles.strategies.Optimistic"
then I get errors resulting from imagekit signals, presumably confused that the file has moved while trying to generate the list_thumbnail
. Here's some of the traceback:
...
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.
I could change the cachefile strategy, but I'd like to keep it. So I'm not sure how to tell the imagekit fields about the file's new location.
It turns out that as well as updating the ImageField
's name
attribute, I had to update its File
object's path
.
So I added this:
self.thumbnail.file.path = new_path
to the save method:
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)