Отложите сохранение поля Django ImageField до тех пор, пока объект не получит pk

У меня есть модель Django для книги, которая имеет поле slug, которое является хэшем, основанным на ее pk. У него также есть thumbnail, который сохраняется по пути, включающему этот slug.

В Admin, если я создаю и сохраняю книгу без эскиза, а затем добавляю эскиз и снова сохраняю книгу, это работает: эскиз сохраняется в /media/books/<slug>/foo.jpg.

НО если я создаю книгу с уменьшенным изображением и сохраняю ее, уменьшенное изображение сохраняется до того, как может быть сгенерировано slug, поэтому оно сохраняется в /media/books/foo.jpg. Это происходит потому, что файлы сохраняются до модели.

Я хотел бы всегда включать slug в путь миниатюры, но не могу понять, как отложить сохранение миниатюры до тех пор, пока не будет сгенерирована slug. Есть идеи?

from django.db import models
from hashes import Hashids


def upload_path(instance, filename):
    return "/".join([books, instance.slug, filename])


class Book(models.Model):
    title = models.CharField(null=False, blank=False, max_length=255)
    slug = models.SlugField(max_length=10, null=False, blank=True)

    thumbnail = models.ImageField(
        upload_to=upload_path, null=False, blank=True, default=""
    )

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

        if not self.slug:
            # Now we have a pk, generate a slug if it doesn't have one.
            hashids = Hashids(salt="my salt", min_length=5)
            self.slug = hashids.encode(self.pk)
            kwargs["force_insert"] = False
            self.save(*args, **kwargs)

(Я знаю, что хэширование небезопасно; кто-то может узнать pk из slug. Я не против этого для данного случая использования.)

Я думаю, что ответ заключается в перемещении файла в нужное место после генерации пк. модель:

import os
from django.conf import settings
from django.db import models
from hashes import Hashids

def upload_path(instance, filename):
    return "/".join([books, instance.slug, filename])

class Book(models.Model):
    # field definitions here

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

        if not self.slug:
            # Now we have a pk, generate a slug if it doesn't have one.
            hashids = Hashids(salt="my salt", min_length=5)
            self.slug = hashids.encode(self.pk)

        if self.thumbnail and f"/{self.slug}/" not in self.thumbnail.path:
            # Move the thumbnail to correct location.
            initial_path = self.thumbnail.path
            filename = os.path.basename(initial_path)
            new_name = upload_path(self, filename)
            new_path = os.path.join(settings.MEDIA_ROOT, new_name)

            if not os.path.exists(os.path.dirname(new_path)):
                # Make the slug directory if it doesn't exist.
                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)
Вернуться на верх