Mar*_*ayo 75 django django-models
我正在Django中构建一个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)
Run Code Online (Sandbox Code Playgroud)
然后,在"python manage.py shell"中我这样做:
song = Song.objects.get(pk=1)
song.delete()
Run Code Online (Sandbox Code Playgroud)
它从数据库中删除,但不从服务器上的文件中删除.我还能尝试什么?
谢谢!
Ant*_*off 122
在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)
Run Code Online (Sandbox Code Playgroud)
save()(例如通过批量更新a QuerySet),旧文件将继续存在,因为信号将无法运行.如果使用传统的文件处理方法,则不会发生这种情况.file字段名称,这不是一个好的样式,因为它与内置file对象标识符冲突.FieldFile.delete()在Django 1.11模型字段引用中(注意它描述了FieldFile类,但是你.delete()直接在字段上调用:FileField实例代理到相应的FieldFile实例,你可以像访问字段一样访问它们的方法)
请注意,删除模型时,不会删除相关文件.如果您需要清理孤立的文件,您需要自己处理它(例如,使用可以手动运行或计划通过例如cron定期运行的自定义管理命令).
为什么Django不会自动删除文件:Django 1.3的发行说明中的条目
在早期的Django版本中,当包含a的模型实例
FileField被删除时,FileField将其自身也从后端存储中删除该文件.这为几个数据丢失场景打开了大门,包括回滚事务和引用同一文件的不同模型上的字段.在Django 1.3,当一个模型被删除FileField的delete()方法将不会被调用.如果您需要清除孤立文件,您需要自己处理它(例如,使用可以手动运行或计划通过例如cron定期运行的自定义管理命令).
un1*_*n1t 53
尝试django-cleanup,当你删除模型时,它会自动调用FileField上的delete方法.
pip install django-cleanup
Run Code Online (Sandbox Code Playgroud)
settings.py
INSTALLED_APPS = (
...
'django_cleanup', # should go after your apps
)
Run Code Online (Sandbox Code Playgroud)
Mes*_*sci 15
您可以.delete使用Django> = 1.10,使用如下所示的文件字段的调用方法从文件系统中删除文件:
obj = Song.objects.get(pk=1)
obj.song.delete()
Run Code Online (Sandbox Code Playgroud)
Ham*_*adi 14
在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.image.name)
super().delete()
Run Code Online (Sandbox Code Playgroud)
它对我来说很容易。如果要在删除前检查文件是否存在,可以使用storage.exists. 如果歌曲存在,egself.song.storage.exists(self.song.name)将返回一个boolean代表。所以它看起来像这样:
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.image.name)
super().delete()
Run Code Online (Sandbox Code Playgroud)
正如@HeyMan 所提到的,使用此解决方案调用Song.objects.all().delete()不会删除文件!发生这种情况是因为Song.objects.all().delete()正在运行Default Manager 的删除查询。因此,如果您希望能够使用objects方法删除模型的文件,则必须编写和使用自定义管理器(仅用于覆盖其删除查询):
class CustomManager(models.Manager):
def delete(self):
for obj in self.get_queryset():
obj.delete()
Run Code Online (Sandbox Code Playgroud)
并且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.image.name)
super().delete()
Run Code Online (Sandbox Code Playgroud)
现在您可以.delete()在objects任何子查询的末尾使用。我写了最简单的CustomManager,但你可以通过返回一些关于你删除的对象或任何你想要的东西来做得更好。
Sha*_*gla 13
您还可以简单地覆盖模型的删除功能以检查文件是否存在并在调用超级函数之前将其删除.
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)
Run Code Online (Sandbox Code Playgroud)
这是一个应用程序,它会在删除模型或上传新文件时删除旧文件: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)
Run Code Online (Sandbox Code Playgroud)
对于那些在较新版本的 Django(当前为 3.1)中寻找答案的人。
我找到了这个网站,它对我有用,无需任何更改,只需将其添加到您的models.py:
from django.db.models.signals import post_delete
from django.dispatch import receiver
from django.db import models
""" Only delete the file if no other instances of that model are using it"""
def delete_file_if_unused(model,instance,field,instance_file_field):
dynamic_field = {}
dynamic_field[field.name] = instance_file_field.name
other_refs_exist = model.objects.filter(**dynamic_field).exclude(pk=instance.pk).exists()
if not other_refs_exist:
instance_file_field.delete(False)
""" Whenever ANY model is deleted, if it has a file field on it, delete the associated file too"""
@receiver(post_delete)
def delete_files_when_row_deleted_from_db(sender, instance, **kwargs):
for field in sender._meta.concrete_fields:
if isinstance(field,models.FileField):
instance_file_field = getattr(instance,field.name)
delete_file_if_unused(sender,instance,field,instance_file_field)
""" Delete the file if something else get uploaded in its place"""
@receiver(pre_save)
def delete_files_when_file_changed(sender,instance, **kwargs):
# Don't run on initial save
if not instance.pk:
return
for field in sender._meta.concrete_fields:
if isinstance(field,models.FileField):
#its got a file field. Let's see if it changed
try:
instance_in_db = sender.objects.get(pk=instance.pk)
except sender.DoesNotExist:
# We are probably in a transaction and the PK is just temporary
# Don't worry about deleting attachments if they aren't actually saved yet.
return
instance_in_db_file_field = getattr(instance_in_db,field.name)
instance_file_field = getattr(instance,field.name)
if instance_in_db_file_field.name != instance_file_field.name:
delete_file_if_unused(sender,instance,field,instance_in_db_file_field)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
43533 次 |
| 最近记录: |