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

Django удалить FileField

Я создаю веб-приложение в Django. У меня есть модель, которая загружает файл, но я не могу его удалить. Вот мой код:

class Song(models.Model):
    name = models.CharField(blank=True, max_length=100)
    author = models.ForeignKey(User, to_field='id', related_name="id_user2")
    song = models.FileField(upload_to='/songs/')
    image = models.ImageField(upload_to='/pictures/', blank=True)
    date_upload = models.DateField(auto_now_add=True)

    def delete(self, *args, **kwargs):
        # You have to prepare what you need before delete the model
        storage, path = self.song.storage, self.song.path
        # Delete the model before the file
        super(Song, self).delete(*args, **kwargs)
        # Delete the file after the model
        storage.delete(path)

Затем в "оболочке python manage.py" я делаю это:

song = Song.objects.get(pk=1)
song.delete()

Удаляет из базы данных, но не файл на сервере. Что еще я могу попробовать?

Спасибо!

4b9b3361

Ответ 1

До Django 1.3 файл удалялся из файловой системы автоматически при удалении соответствующего экземпляра модели. Вероятно, вы используете более новую версию Django, поэтому вам придется самостоятельно удалить файл из файловой системы.

Вы можете сделать это несколькими способами, один из которых использует сигнал pre_delete или post_delete.

Пример

В настоящее время мой метод выбора - это сочетание сигналов post_delete и pre_save, что позволяет удалять устаревшие файлы при удалении соответствующих моделей или изменении их файлов.

Основано на гипотетической модели MediaFile:

import os
import uuid

from django.db import models
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _


class MediaFile(models.Model):
    file = models.FileField(_("file"),
        upload_to=lambda instance, filename: str(uuid.uuid4()))


# These two auto-delete files from filesystem when they are unneeded:

@receiver(models.signals.post_delete, sender=MediaFile)
def auto_delete_file_on_delete(sender, instance, **kwargs):
    """
    Deletes file from filesystem
    when corresponding 'MediaFile' object is deleted.
    """
    if instance.file:
        if os.path.isfile(instance.file.path):
            os.remove(instance.file.path)

@receiver(models.signals.pre_save, sender=MediaFile)
def auto_delete_file_on_change(sender, instance, **kwargs):
    """
    Deletes old file from filesystem
    when corresponding 'MediaFile' object is updated
    with new file.
    """
    if not instance.pk:
        return False

    try:
        old_file = MediaFile.objects.get(pk=instance.pk).file
    except MediaFile.DoesNotExist:
        return False

    new_file = instance.file
    if not old_file == new_file:
        if os.path.isfile(old_file.path):
            os.remove(old_file.path)
  • Крайний случай: если ваше приложение загружает новый файл и указывает экземпляр модели на новый файл без вызова save() (например, путем массового обновления QuerySet), старый файл будет лежать без дела, потому что сигналы не будут запущены. Этого не происходит, если вы используете обычные методы обработки файлов.
  • Я думаю, что одно из созданных мной приложений имеет этот код в рабочем состоянии, но тем не менее использует его на свой страх и риск.
  • Стиль кодирования: в этом примере в качестве имени поля используется file, что не является хорошим стилем, поскольку оно конфликтует со встроенным идентификатором объекта file.

Смотрите также

  • FieldFile.delete() в ссылке на поле модели Django 1.11 (обратите внимание, что она описывает класс FieldFile, но вы вызываете .delete() непосредственно в поле: экземпляр FileField проксирует соответствующий экземпляр FieldFile), и вы получаете доступ его методы, как если бы они были полями)

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

  • Почему Django не удаляет файлы автоматически: запись в заметках о выпуске для Django 1.3

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

  • Пример использования только сигнала pre_delete

Ответ 2

Попробуйте django-cleanup, он автоматически вызывает метод удаления на FileField при удалении модели.

pip install django-cleanup

settings.py

INSTALLED_APPS = (
     ...
    'django_cleanup', # should go after your apps
)

Ответ 3

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

import os

class Excel(models.Model):
    upload_file = models.FileField(upload_to='/excels/', blank =True)   
    uploaded_on = models.DateTimeField(editable=False)


    def delete(self,*args,**kwargs):
        if os.path.isfile(self.upload_file.path):
            os.remove(self.upload_file.path)

        super(Excel, self).delete(*args,**kwargs)

Ответ 4

Вы можете удалить файл из файловой системы, вызвав метод .delete для поля файла, как показано ниже, с помощью Django> = 1.10:

obj = Song.objects.get(pk=1)
obj.song.delete()

Ответ 5

@Антон Строгонов

Я пропускаю что-то в коде при изменении файла, если вы создаете новый файл, генерируете ошибку, потому что новый файл a не нашел пути. Я изменил код функции и добавил предложение try/except, и он работает хорошо.

@receiver(models.signals.pre_save, sender=MediaFile)
def auto_delete_file_on_change(sender, instance, **kwargs):
    """Deletes file from filesystem
    when corresponding `MediaFile` object is changed.
    """
    if not instance.pk:
        return False

    try:
        old_file = MediaFile.objects.get(pk=instance.pk).file
    except MediaFile.DoesNotExist:
        return False

    new_file = instance.file
    if not old_file == new_file:
        try:
            if os.path.isfile(old_file.path):
                os.remove(old_file.path)
        except Exception:
            return False

Ответ 6

Вот приложение, которое удаляет старые файлы всякий раз, когда удаляется модель, или загружается новый файл: django-smartfields

from django.db import models
from smartfields import fields

class Song(models.Model):
    song = fields.FileField(upload_to='/songs/')
    image = fields.ImageField(upload_to='/pictures/', blank=True)

Ответ 7

Этот код будет запускаться каждый раз, когда я загружаю новое изображение (поле с логотипом) и проверяю, существует ли логотип, если это так, закройте его и удалите с диска. Разумеется, такая же процедура может быть выполнена в функции приемника. Надеюсь, это поможет.

 #  Returns the file path with a folder named by the company under /media/uploads
    def logo_file_path(instance, filename):
        company_instance = Company.objects.get(pk=instance.pk)
        if company_instance.logo:
            logo = company_instance.logo
            if logo.file:
                if os.path.isfile(logo.path):
                    logo.file.close()
                    os.remove(logo.path)

        return 'uploads/{0}/{1}'.format(instance.name.lower(), filename)


    class Company(models.Model):
        name = models.CharField(_("Company"), null=False, blank=False, unique=True, max_length=100) 
        logo = models.ImageField(upload_to=logo_file_path, default='')