Django删除FileField
我正在Django中构build一个Web应用程序。 我有一个模型,上传一个文件,但我不能删除它。 这是我的代码:
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 shell”中我这样做:
song = Song.objects.get(pk=1) song.delete()
它从数据库中删除而不是在服务器上的文件。 我还有什么可以尝试?
谢谢!
在Django 1.3之前,当您删除相应的模型实例时,该文件将自动从文件系统中删除。 你可能正在使用一个更新的Django版本,所以你必须实现从文件系统自己删除文件。
你可以用几种方法做到这一点,其中之一是使用pre_delete
或post_delete
信号。
例
我select的方法目前是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
对象标识冲突。
也可以看看
-
FileField.delete()
(Django 1.11模型字段参考)请注意,删除模型时,相关文件不会被删除。 如果您需要清理孤立的文件,您需要自己处理(例如,使用可以手动运行的自定义pipe理命令或计划通过例如cron定期运行的命令)。
-
为什么Django不会自动删除文件: 在Django 1.3的发行说明中input
在较早的Django版本中,当包含
FileField
的模型实例被删除时,FileField
自己也将其从后端存储中删除。 这为几个数据丢失情况打开了大门,包括回滚事务和引用同一文件的不同模型上的字段。 在Django 1.3中,当一个模型被删除时,FileField
的delete()
方法将不会被调用。 如果需要清理孤立文件,则需要自行处理(例如,使用自定义pipe理命令,可以手动运行或计划通过例如cron定期运行)。 -
仅使用
pre_delete
信号的示例
尝试Django的清理 ,它会自动调用FileField删除模型时删除方法。
pip install django-cleanup
settings.py
INSTALLED_APPS = ( ... 'django_cleanup', # should go after your apps )
您也可以简单地覆盖模型的删除function,以检查文件是否存在,并在调用超级函数之前将其删除。
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)
您可以使用Django 1.10调用文件字段的.delete
方法从文件系统中删除文件,如下所示:
obj = Song.objects.get(pk=1) obj.song.delete()
@安顿Strogonoff
我在代码中丢失了某些东西的时候,文件发生了变化,如果你创build了一个新文件会产生一个错误,因为一个新文件没有find一个path。 我修改了函数的代码,并添加了一个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
这是一个应用程序,将删除旧的文件,无论何时删除模型或上传新文件: 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)
每当我上传一个新的图像(标志字段),这个代码将运行,并检查是否已经存在的标志,如果是这样,closures它,并从磁盘中删除它。 接收机function当然可以采用相同的程序。 希望这可以帮助。
# 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='')