Я создаю веб-приложение на 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()
Он удаляет из базы данных, но не файл на сервере. Что еще можно попробовать?
Благодарность!
django
django-models
Маркос Агуайо
источник
источник
Ответы:
До 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()
(например, путем массового обновления aQuerySet
), старый файл будет лежать без дела, потому что сигналы не будут запускаться. Этого не произойдет, если вы используете обычные методы обработки файлов.file
имя поля, что не является хорошим стилем, поскольку он конфликтует со встроеннымfile
идентификатором объекта.Смотрите также
FieldFile.delete()
в справочнике по полям модели Django 1.11 (обратите внимание, что он описываетFieldFile
класс, но вы вызываете.delete()
непосредственно в поле:FileField
экземпляр прокси к соответствующемуFieldFile
экземпляру, и вы получаете доступ к его методам, как если бы они были полями)Почему Django не удаляет файлы автоматически: запись в примечаниях к выпуску Django 1.3
Пример использования
pre_delete
только сигналаисточник
instance.song.delete(save=False)
, поскольку он использует правильный механизм хранения django.os.path.isfile(old_file.path)
сбой, посколькуold_file.path
возникает ошибка (с полем не связан файл). Я исправил это, добавивif old_file:
непосредственно перед вызовомos.path.isfile()
.Попробуйте django-cleanup , он автоматически вызывает метод удаления в FileField, когда вы удаляете модель.
pip install django-cleanup
settings.py
INSTALLED_APPS = ( ... 'django_cleanup', # should go after your apps )
источник
Вы можете удалить файл из файловой системы с помощью
.delete
метода вызова поля файла, показанного ниже, с Django> = 1.10:obj = Song.objects.get(pk=1) obj.song.delete()
источник
Вы также можете просто перезаписать функцию удаления модели, чтобы проверить наличие файла и удалить его перед вызовом суперфункции.
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)
источник
queryset.delete()
не очистит файлы с помощью этого решения. Вам нужно будет выполнить итерацию по набору запросов и вызвать.delete()
каждый объект.Решение Django 2.x:
Обработать удаление файлов в Django 2 очень просто . Я пробовал следующее решение с использованием Django 2 и SFTP Storage, а также FTP STORAGE, и я почти уверен, что оно будет работать с любыми другими менеджерами хранилища, которые реализовали
delete
метод. (delete
метод - один изstorage
абстрактных методов.)Переопределите
delete
метод модели таким образом, чтобы экземпляр удалял свои FileFields перед удалением самого себя: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, using=None, keep_parents=False): self.song.storage.delete(self.song.name) self.image.storage.delete(self.song.name) super().delete()
Для меня это работает довольно легко. Если вы хотите проверить, существует ли файл перед удалением, вы можете использовать
storage.exists
. Напримерself.song.storage.exists(self.song.name)
, вернетboolean
представление, существует ли песня. Вот так это будет выглядеть:def delete(self, using=None, keep_parents=False): # assuming that you use same storage for all files in this model: storage = self.song.storage if storage.exists(self.song.name): storage.delete(self.song.name) if storage.exists(self.image.name): storage.delete(self.song.name) super().delete()
РЕДАКТИРОВАТЬ (в дополнение):
Как упоминал @HeyMan , при вызове этого решения
Song.objects.all().delete()
файлы не удаляются! Это происходит из-за того, чтоSong.objects.all().delete()
выполняется запрос на удаление диспетчера по умолчанию . Поэтому, если вы хотите иметь возможность удалять файлы модели с помощьюobjects
методов, вы должны написать и использовать Custom Manager (просто для переопределения его запроса на удаление):class CustomManager(models.Manager): def delete(self): for obj in self.get_queryset(): obj.delete()
и для присвоения
CustomManager
модели вы должны инициализироватьobjects
внутри своей модели: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) objects = CustomManager() # just add this line of code inside of your model def delete(self, using=None, keep_parents=False): self.song.storage.delete(self.song.name) self.image.storage.delete(self.song.name) super().delete()
Теперь вы можете использовать
.delete()
в конце любыхobjects
подзапросов. Я написал самое простоеCustomManager
, но вы можете сделать это лучше, вернув что-нибудь об удаленных вами объектах или о том, что вы хотите.источник
Вот приложение, которое удаляет старые файлы всякий раз, когда модель удаляется или загружается новый файл: 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)
источник
@Anton Strogonoff
Мне что-то не хватает в коде при изменении файла, если вы создаете новый файл, возникает ошибка, потому что новый файл не нашел путь. Я изменил код функции и добавил предложение 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
источник
try:
блоке (AttributeError
возможно?).Этот код будет запускаться каждый раз, когда я загружаю новое изображение (поле логотипа) и проверяю, существует ли уже логотип, если это так, закройте его и удалите с диска. Та же самая процедура, конечно, может быть проделана в функции приемника. Надеюсь это поможет.
# 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='')
источник