Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从数据库/模型中删除对象时,如何让Django Admin删除文件?_Django_File_Storage_Django Models_Imagefield - Fatal编程技术网

从数据库/模型中删除对象时,如何让Django Admin删除文件?

从数据库/模型中删除对象时,如何让Django Admin删除文件?,django,file,storage,django-models,imagefield,Django,File,Storage,Django Models,Imagefield,我将1.2.5与标准ImageField一起使用,并使用内置的存储后端。文件上传很好,但当我从管理员中删除条目时,服务器上的实际文件不会被删除。此功能将在Django 1.3中删除,因此我不会依赖它 您可以覆盖相关模型的delete方法,以便在从数据库中完全删除条目之前删除文件 编辑: 下面是一个简单的例子 class MyModel(models.Model): self.somefile = models.FileField(...) def delete(self, *

我将1.2.5与标准ImageField一起使用,并使用内置的存储后端。文件上传很好,但当我从管理员中删除条目时,服务器上的实际文件不会被删除。

此功能将在Django 1.3中删除,因此我不会依赖它

您可以覆盖相关模型的
delete
方法,以便在从数据库中完全删除条目之前删除文件

编辑:

下面是一个简单的例子

class MyModel(models.Model):

    self.somefile = models.FileField(...)

    def delete(self, *args, **kwargs):
        somefile.delete()

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

可以考虑使用Pyl删除或POST删除信号:

当然,删除FileField自动删除的相同原因也适用于此处。如果删除在其他地方引用的文件,则会出现问题

在我的情况下,这似乎是合适的,因为我有一个专用的文件模型来管理我的所有文件


注意:由于某些原因,post_delete似乎无法正常工作。文件被删除了,但数据库记录保留了下来,这与我所期望的完全相反,即使在出错的情况下也是如此。pre_delete工作正常。

这段代码在Django 1.4上运行良好,还带有管理面板

class ImageModel(models.Model):
    image = ImageField(...)

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

在删除模型之前获取存储和路径很重要,否则如果删除模型,后者也将保持无效。

您可以收到
pre\u delete
post\u delete
信号(请参见下面的@toto\u tico注释)并调用FileField对象上的方法,因此(在models.py中):


我可能有一个特殊的情况,因为我在我的文件字段中使用带有动态目录名的upload_to选项,但我找到的解决方案是使用os.rmdir

在模型中:

import os

确保在文件前写“self”。所以上面的例子应该是

def delete(self, *args, **kwargs):
        self.somefile.delete()

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

我忘记了文件之前的“self”,它在全局名称空间中查找时无法工作。

Django 1.5解决方案:我使用post_delete的原因有很多,这些都是我应用程序内部的原因

from django.db.models.signals import post_delete
from django.dispatch import receiver

@receiver(post_delete, sender=Photo)
def photo_post_delete_handler(sender, **kwargs):
    photo = kwargs['instance']
    storage, path = photo.original_image.storage, photo.original_image.path
    storage.delete(path)
我把这个贴在models.py文件的底部


original\u image
字段是我的
Photo
模型中的
ImageField

可能有点晚了。但对我来说最简单的方法是使用post_保存信号。请记住,即使在QuerySet删除过程中,信号也会被排除,但是[model].delete()方法在QuerySet删除过程中不会被排除,因此它不是覆盖它的最佳选项

core/models.py:

class MyModel(models.Model):
    file = models.FileField()
    ...

# Receive the pre_delete signal and delete the file associated with the model instance.
from django.db.models.signals import pre_delete
from django.dispatch.dispatcher import receiver

@receiver(pre_delete, sender=MyModel)
def mymodel_delete(sender, instance, **kwargs):
    # Pass false so FileField doesn't save the model.
    instance.file.delete(False)
from django.db import models
from django.db.models.signals import post_delete
from core.signals import delete_image_slide
SLIDE1_IMGS = 'slide1_imgs/'

class Slide1(models.Model):
    title = models.CharField(max_length = 200)
    description = models.CharField(max_length = 200)
    image = models.ImageField(upload_to = SLIDE1_IMGS, null = True, blank = True)
    video_embed = models.TextField(null = True, blank = True)
    enabled = models.BooleanField(default = True)

"""---------------------------- SLIDE 1 -------------------------------------"""
post_delete.connect(delete_image_slide, Slide1)
"""--------------------------------------------------------------------------"""
core/signals.py

import os

def delete_image_slide(sender, **kwargs):
    slide = kwargs.get('instance')
    try:
        os.remove(slide.image.path)
    except:
        pass

使用post_delete肯定是正确的方法。虽然有时事情会出错,文件不会被删除。当然,在使用post_delete之前,有一堆旧文件没有被删除。我创建了一个函数,可以根据对象引用的文件删除对象的文件。如果对象引用的文件不存在,则删除对象。如果文件没有对象,则还可以删除,还可以根据对象的“活动”标志删除。。我在大多数模型中添加了一些东西。必须向其传递要检查的对象、对象文件的路径、文件字段和删除非活动对象的标志:

def cleanup_model_objects(m_objects, model_path, file_field='image', clear_inactive=False):
    # PART 1 ------------------------- INVALID OBJECTS
    #Creates photo_file list based on photo path, takes all files there
    model_path_list = os.listdir(model_path)

    #Gets photo image path for each photo object
    model_files = list()
    invalid_files = list()
    valid_files = list()
    for obj in m_objects:

        exec("f = ntpath.basename(obj." + file_field + ".path)")  # select the appropriate file/image field

        model_files.append(f)  # Checks for valid and invalid objects (using file path)
        if f not in model_path_list:
            invalid_files.append(f)
            obj.delete()
        else:
            valid_files.append(f)

    print "Total objects", len(model_files)
    print "Valid objects:", len(valid_files)
    print "Objects without file deleted:", len(invalid_files)

    # PART 2 ------------------------- INVALID FILES
    print "Files in model file path:", len(model_path_list)

    #Checks for valid and invalid files
    invalid_files = list()
    valid_files = list()
    for f in model_path_list:
        if f not in model_files:
            invalid_files.append(f)
        else:
            valid_files.append(f)
    print "Valid files:", len(valid_files)
    print "Files without model object to delete:", len(invalid_files)

    for f in invalid_files:
        os.unlink(os.path.join(model_path, f))

    # PART 3 ------------------------- INACTIVE PHOTOS
    if clear_inactive:
        #inactive_photos = Photo.objects.filter(active=False)
        inactive_objects = m_objects.filter(active=False)
        print "Inactive Objects to Delete:", inactive_objects.count()
        for obj in inactive_objects:
            obj.delete()
    print "Done cleaning model."
以下是您可以使用的方法:

photos = Photo.objects.all()
photos_path, tail = ntpath.split(photos[0].image.path)  # Gets dir of photos path, this may be different for you
print "Photos -------------->"
cleanup_model_objects(photos, photos_path, file_field='image', clear_inactive=False)  # image file is default
试一试

设置.py

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

您需要在
删除
更新
上删除实际文件

from django.db import models

class MyImageModel(models.Model):
    image = models.ImageField(upload_to='images')

    def remove_on_image_update(self):
        try:
            # is the object in the database yet?
            obj = MyImageModel.objects.get(id=self.id)
        except MyImageModel.DoesNotExist:
            # object is not in db, nothing to worry about
            return
        # is the save due to an update of the actual image file?
        if obj.image and self.image and obj.image != self.image:
            # delete the old image file from the storage in favor of the new file
            obj.image.delete()

    def delete(self, *args, **kwargs):
        # object is being removed from db, remove the file from storage first
        self.image.delete()
        return super(MyImageModel, self).delete(*args, **kwargs)

    def save(self, *args, **kwargs):
        # object is possibly being updated, if so, clean up.
        self.remove_on_image_update()
        return super(MyImageModel, self).save(*args, **kwargs)

如果您的项目中已存在大量未使用的文件,并且希望删除这些文件,则可以使用django实用程序

django 2.x解决方案: 没有必要安装任何软件包!在Django 2中处理它非常容易。我尝试了使用Django 2和SFTP存储的以下解决方案(但我认为它可以与任何存储一起使用)

首先写一封信。因此,如果您希望能够使用
对象
方法删除模型的文件,则必须编写并使用[Custom Manager][3](用于覆盖
对象
删除()
方法):

现在,您必须先删除
图像
,然后再删除模型本身,要将
CustomManager
分配给模型,您必须在模型内初始化
对象

class MyModel(models.Model):
    image = models.ImageField(upload_to='/pictures/', blank=True)
    objects = CustomManager() # add CustomManager to model
    def delete(self, using=None, keep_parents=False):

    objects = CustomManager() # just add this line of code inside of your model

    def delete(self, using=None, keep_parents=False):
        self.image.storage.delete(self.song.name)
        super().delete()

嗯,实际上应该是这样。检查上传文件夹的文件权限(更改为0777)。Django删除了自动删除功能(针对看到上述评论的Google用户)。您有没有示例说明如何在模型中使用该功能来删除文件?我正在查看文档,看到了如何从数据库中删除对象的示例,但没有看到任何关于文件删除的实现。这种方法是错误的,因为它不适用于批量删除(如管理员的“删除选定项”功能)。例如
MyModel.objects.all()[0].delete()
将删除该文件,而
MyModel.objects.all().delete()
将不删除该文件。使用signals.ally
post\u delete
可能不起作用,因为
file\u field.delete()
默认情况下会将模型保存到db,请尝试
file\u field.delete(False)
这对我不起作用(Django 1.5),Django 1.3变更日志声明:“在Django 1.3中,删除模型时,文件字段的delete()方法不会被调用。如果需要清理孤立文件,则需要自己处理(例如,使用自定义管理命令,该命令可以手动运行,也可以通过cron定期运行)。“此解决方案错误<代码>删除
在删除行时并不总是被调用,您必须使用信号。请确保添加一个检查,如果
实例.file
字段不为空,或者它可以(至少尝试)删除整个媒体根目录。这甚至适用于
ImageField(null=False)
fields。谢谢。一般来说,我建议使用
post_delete
信号,因为无论出于何种原因,删除失败都会更加安全。然后,模型和文件都不会被删除,从而保持数据的一致性。如果我对
post\u delete
pre\u delete
sig的理解有误,请纠正我
from django.db import models

class MyImageModel(models.Model):
    image = models.ImageField(upload_to='images')

    def remove_on_image_update(self):
        try:
            # is the object in the database yet?
            obj = MyImageModel.objects.get(id=self.id)
        except MyImageModel.DoesNotExist:
            # object is not in db, nothing to worry about
            return
        # is the save due to an update of the actual image file?
        if obj.image and self.image and obj.image != self.image:
            # delete the old image file from the storage in favor of the new file
            obj.image.delete()

    def delete(self, *args, **kwargs):
        # object is being removed from db, remove the file from storage first
        self.image.delete()
        return super(MyImageModel, self).delete(*args, **kwargs)

    def save(self, *args, **kwargs):
        # object is possibly being updated, if so, clean up.
        self.remove_on_image_update()
        return super(MyImageModel, self).save(*args, **kwargs)
class CustomManager(models.Manager):
    def delete(self):
        for obj in self.get_queryset():
            obj.delete()
class MyModel(models.Model):
    image = models.ImageField(upload_to='/pictures/', blank=True)
    objects = CustomManager() # add CustomManager to model
    def delete(self, using=None, keep_parents=False):

    objects = CustomManager() # just add this line of code inside of your model

    def delete(self, using=None, keep_parents=False):
        self.image.storage.delete(self.song.name)
        super().delete()