удалить потерянный файл из модели - PullRequest
1 голос
/ 30 апреля 2020

У меня есть следующая модель:

class Class(models.Model):
    title = models.CharField(max_length = 60)

    video = models.FileField(
        upload_to = class_files_custom_upload, 
        validators = [
            FileExtensionValidator(['mp4', 'webm', 'mpg', 'mpeg', 'ogv']),
        ]
    )

    section =  models.ForeignKey(Section, on_delete = models.CASCADE)
    created = models.DateTimeField(auto_now_add = True)

    class Meta:
        verbose_name = 'clase'
        verbose_name_plural = 'clases'
        ordering = ['created']

    def __str__(self):
        return self.title

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

Для этого я настраиваю загрузку файла, помещая вызываемый элемент в upload_to:

def class_files_custom_upload(instance, filename):
    try:
        old_instance = Class.objects.get(id = instance.id)
        old_instance.video.delete()
    except Class.DoesNotExist:
        pass

    return os.path.join('courses/videos', generate_secure_filename(filename))

Таким образом, я достигаю своей цели. Но у меня есть несколько моделей, которые сохраняют мультимедийные файлы, и для каждой из них мне нужно настроить загрузку файла, практически выполняя функцию, почти равную class_files_custom_upload, и код повторяется, и это совсем не оптимально.

Я пытался создать функцию многократного использования, которая соответствует цели функции class_files_custom_upload, в различных полях, таких как ImageField и FileField, но я не могу этого сделать, поскольку функция получает только 2 параметра, instance и filename, что является слишком малым количеством данных для его достижения.

Единственный способ, которым мне удалось создать эту "функцию", которая соответствует цели и может быть повторно использована, - это создать валидатор:

def delete_orphaned_media_file(value):
    old_instance = value.instance.__class__.objects.get(pk = value.instance.pk)
    media_file_field = getattr(old_instance, value.field.name)

    if not media_file_field.name == value.name: media_file_field.delete()

И это работает, но в конце концов это «валидатор», «валидатор» должен проверять поле, а не «это». У меня вопрос: это хорошая практика?

Есть ли лучшая альтернатива моему решению? но эта альтернатива отвечает цели повторного использования.

Любое предложение помогает моему обучению, спасибо.

1 Ответ

0 голосов
/ 30 апреля 2020

Одна из проблем заключается в том, что два или более FileField могут ссылаться на один и тот же файл. В базе данных FileField хранит местоположение файла, поэтому два или более столбцов могут иметь один и тот же файл, поэтому простое удаление старого не является (полностью) безопасным.

Вы можете, например, сделать использование django-unused-media. Вы устанавливаете это с:

$ pip install <b>django-unused-media</b>

Далее вы добавляете это к установленным приложениям:

# settings.py

INSTALLED_APPS = [
    # &hellip;,
    <b>'django_unused_media'</b>,
    # &hellip;
]

Далее вы можете запустить:

python3 manage.py <b>cleanup_unused_media</b>

это будет искать файлы, на которые больше нет ссылок, и очищайте их в интерактивном режиме.

Вы также можете создать запланированное задание (например, с cron), которое запускается с флагом --no-input:

python3 manage.py cleanup_unused_media <b>--no-input</b>
...