Подтвердить что ты не робот

Замена изображения Django не удаляет оригинальные

В Django, если у вас есть ImageFile в модели, удаление удалит связанный файл с диска, а также удалит запись из базы данных.

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

Теперь удаление объекта не удалит исходный файл только с заменой.

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

4b9b3361

Ответ 1

Лучшей стратегией, которую я нашел, является создание пользовательского метода сохранения в модели:

class Photo(models.Model):

    image = ImageField(...) # works with FileField also

    def save(self, *args, **kwargs):
        # delete old file when replacing by updating the file
        try:
            this = Photo.objects.get(id=self.id)
            if this.image != self.image:
                this.image.delete(save=False)
        except: pass # when new photo then we do nothing, normal case          
        super(Photo, self).save(*args, **kwargs)

И будьте осторожны, как и при обновлении, которое не удаляет задний файл, удаление модели экземпляра (здесь Фото) будет не удалять внутренний файл, а не Django 1.3, вам нужно будет добавить дополнительный код для этого (или регулярно выполнять грязное задание cron).

Наконец, проверьте все свои случаи обновления/удаления с помощью отношений ForeignKey, ManytoMany и других, чтобы проверить, правильно ли удалены исходные файлы. Верьте только, что вы тестируете.

Ответ 2

Не следует ли заменять изображение также удалить ненужный файл с диска?

В прежние времена FileField стремился очистить осиротевшие файлы. Но это изменилось в Django 1.2:

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

Ответ 3

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

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

Добавьте следующий класс:

from django.core.files.storage import FileSystemStorage
class OverwriteStorage(FileSystemStorage):
    def _save(self, name, content):
        if self.exists(name):
            self.delete(name)
        return super(OverwriteStorage, self)._save(name, content)

    def get_available_name(self, name):
        return name

И используйте его с ImageField следующим образом:

class MyModel(models.Model):
    myfield = models.ImageField(
        'description of purpose',
        upload_to='folder_name',
        storage=OverwriteStorage(),  ### using OverwriteStorage here
        max_length=500,
        null=True,
        blank=True,
        height_field='height',
        width_field='width'
    )
    height = models.IntegerField(blank=True, null=True)
    width = models.IntegerField(blank=True, null=True)

Ответ 4

Если вы не используете транзакции или не боитесь потерять файлы при откат транзакции, вы можете использовать django-cleanup

Ответ 5

В этой проблеме было несколько билетов, хотя, скорее всего, это не повлияет на это. Наиболее полное http://code.djangoproject.com/ticket/11663. Патчи и комментарии к билетам могут дать вам какое-то направление, если вы ищете решение.

Вы также можете рассмотреть использование другой StorageBackend, такой как перезаписываемая система хранения файлов, предоставленная Django snippet 976. http://djangosnippets.org/snippets/976/. Вы можете изменить хранилище по умолчанию на этот сервер или вы можете переопределить его в каждом объявлении FileField/ImageField.

Ответ 6

Вот код, который может работать с или без upload_to=... или blank=True, а также если файл с таким же именем, как и старый.

(синтаксис py3, проверенный на Django 1.7)

class Attachment(models.Model):

    document = models.FileField(...)  # or ImageField

    def delete(self, *args, **kwargs):
        self.document.delete(save=False)
        super().delete(*args, **kwargs)

    def save(self, *args, **kwargs):
        if self.pk:
            old = self.__class__._default_manager.get(pk=self.pk)
            if old.document.name and (not self.document._committed or not self.document.name):
                old.document.delete(save=False)
        super().save(*args, **kwargs)

Помните, что такое решение применимо только в том случае, если вы находитесь в не транзакционном контексте (нет отката, потому что файл окончательно потерян)

Ответ 7

Я использовал простой метод с popen, поэтому, когда я сохраняю мою модель Info, я удаляю прежний файл перед связыванием с новым:

import os

try:
    os.popen("rm %s" % str(info.photo.path))
except:
    #deal with error
    pass
info.photo = nd['photo']