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

时间:2022-09-04 18:01:33

I am using 1.2.5 with a standard ImageField and using the built-in storage backend. Files upload fine but when I remove an entry from admin the actual file on the server does not delete.

我正在使用1.2.5标准的ImageField和内置的存储后端。文件上传很好,但是当我从管理中删除一个条目时,服务器上的实际文件不会删除。

12 个解决方案

#1


87  

You can receive the pre_delete or post_delete signal (see @toto_tico's comment below) and call the delete method on the FileField object, thus (in models.py):

您可以接收pre_delete或post_delete信号(参见下面的@toto_tico注释),并在FileField对象上调用delete方法(在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)

#2


33  

Django 1.5 solution: I use post_delete for various reasons that are internal to my app.

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)

I stuck this at the bottom of the models.py file.

我把这个贴在模型的底部。py文件。

the original_image field is the ImageField in my Photo model.

original_image字段是我的照片模型中的ImageField。

#3


32  

Try django-cleanup

尝试django-cleanup

pip install django-cleanup

settings.py

settings.py

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

#4


17  

This code runs well on Django 1.4 also with the Admin panel.

该代码在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)

It's important to get the storage and the path before delete the model or the latter will persist void also if deleted.

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

#5


7  

You need to remove the actual file on both delete and update.

您需要删除删除和更新的实际文件。

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)

#6


6  

You may consider using a pre_delete or post_delete signal:

您可以考虑使用pre_delete或post_delete信号:

https://docs.djangoproject.com/en/dev/topics/signals/

https://docs.djangoproject.com/en/dev/topics/signals/

Of course, the same reasons that FileField automatic deletion was removed also apply here. If you delete a file that is referenced somewhere else you will have problems.

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

In my case this seemed appropriate because I had a dedicated File model to manage all of my files.

在我的例子中,这似乎是合适的,因为我有一个专门的文件模型来管理我的所有文件。

Note: For some reason post_delete doesn't seem to work right. The file got deleted, but the database record stayed, which is completely the opposite of what I would expect, even under error conditions. pre_delete works fine though.

注意:出于某种原因,post_delete似乎不能正常工作。文件被删除,但是数据库记录保持不变,这与我预期的完全相反,即使是在错误条件下。不过pre_delete没问题。

#7


2  

Maybe it's a little late. But the easiest way for me is to use a post_save signal. Just to remember that signals are excecuted even during a QuerySet delete process, but the [model].delete() method is not excecuted during the QuerySet delete process, so it's not the best option to override it.

可能有点晚了。但是对我来说最简单的方法是使用post_save信号。只是为了记住,即使在QuerySet删除过程中,信号也会被取消,但是[model].delete()方法在QuerySet删除过程中不会被取消,所以重写它不是最好的选择。

core/models.py:

核心/ models.py:

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

核心/ signals.py

import os

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

#8


1  

This functionality will be removed in Django 1.3 so I wouldn't rely on it.

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

You could override the delete method of the model in question to delete the file before removing the entry from the database completely.

在完全从数据库中删除条目之前,可以重写模型的delete方法来删除文件。

Edit:

编辑:

Here is a quick example.

这里有一个简单的例子。

class MyModel(models.Model):

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

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

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

#9


1  

Using the post_delete is for sure the right way to go. Sometimes though things can go wrong, and files don't get deleted. There is of course the case that you have a bunch of old files that weren't deleted before post_delete was used. I created a function that deletes files for objects based on if the file the object references does not exist then delete object, if the file does not have an object, then also delete, also it can delete based on an "active" flag for an object.. Something I added to most of my models. You have to pass it the objects you want to check, the path to the objects files, the file field and a flag to delete inactive objects:

使用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."

This is how you can use this:

这就是你如何使用它:

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

#10


0  

make sure you write "self" before the file. so example above should be

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

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

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

I've forgotten the "self" before my file and that didn't work as it was looking in the global namespace.

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

#11


0  

If you already have number of unused files in your project and want to delete them, you can use django utility django-unused-media

如果您的项目中已经有许多未使用的文件,并且想要删除它们,您可以使用django实用程序django unuse -media

#12


-1  

I may have a special case since I am using the upload_to option on my file field with dynamic directory names but the solution I found was to use os.rmdir.

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

In models:

在模型:

import os

...

class Some_Model(models.Model):
     save_path = models.CharField(max_length=50)
     ...
     def delete(self, *args,**kwargs):
          os.rmdir(os.path.join(settings.MEDIA_ROOT, self.save_path)
          super(Some_Model,self).delete(*args, **kwargs)

#1


87  

You can receive the pre_delete or post_delete signal (see @toto_tico's comment below) and call the delete method on the FileField object, thus (in models.py):

您可以接收pre_delete或post_delete信号(参见下面的@toto_tico注释),并在FileField对象上调用delete方法(在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)

#2


33  

Django 1.5 solution: I use post_delete for various reasons that are internal to my app.

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)

I stuck this at the bottom of the models.py file.

我把这个贴在模型的底部。py文件。

the original_image field is the ImageField in my Photo model.

original_image字段是我的照片模型中的ImageField。

#3


32  

Try django-cleanup

尝试django-cleanup

pip install django-cleanup

settings.py

settings.py

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

#4


17  

This code runs well on Django 1.4 also with the Admin panel.

该代码在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)

It's important to get the storage and the path before delete the model or the latter will persist void also if deleted.

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

#5


7  

You need to remove the actual file on both delete and update.

您需要删除删除和更新的实际文件。

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)

#6


6  

You may consider using a pre_delete or post_delete signal:

您可以考虑使用pre_delete或post_delete信号:

https://docs.djangoproject.com/en/dev/topics/signals/

https://docs.djangoproject.com/en/dev/topics/signals/

Of course, the same reasons that FileField automatic deletion was removed also apply here. If you delete a file that is referenced somewhere else you will have problems.

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

In my case this seemed appropriate because I had a dedicated File model to manage all of my files.

在我的例子中,这似乎是合适的,因为我有一个专门的文件模型来管理我的所有文件。

Note: For some reason post_delete doesn't seem to work right. The file got deleted, but the database record stayed, which is completely the opposite of what I would expect, even under error conditions. pre_delete works fine though.

注意:出于某种原因,post_delete似乎不能正常工作。文件被删除,但是数据库记录保持不变,这与我预期的完全相反,即使是在错误条件下。不过pre_delete没问题。

#7


2  

Maybe it's a little late. But the easiest way for me is to use a post_save signal. Just to remember that signals are excecuted even during a QuerySet delete process, but the [model].delete() method is not excecuted during the QuerySet delete process, so it's not the best option to override it.

可能有点晚了。但是对我来说最简单的方法是使用post_save信号。只是为了记住,即使在QuerySet删除过程中,信号也会被取消,但是[model].delete()方法在QuerySet删除过程中不会被取消,所以重写它不是最好的选择。

core/models.py:

核心/ models.py:

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

核心/ signals.py

import os

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

#8


1  

This functionality will be removed in Django 1.3 so I wouldn't rely on it.

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

You could override the delete method of the model in question to delete the file before removing the entry from the database completely.

在完全从数据库中删除条目之前,可以重写模型的delete方法来删除文件。

Edit:

编辑:

Here is a quick example.

这里有一个简单的例子。

class MyModel(models.Model):

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

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

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

#9


1  

Using the post_delete is for sure the right way to go. Sometimes though things can go wrong, and files don't get deleted. There is of course the case that you have a bunch of old files that weren't deleted before post_delete was used. I created a function that deletes files for objects based on if the file the object references does not exist then delete object, if the file does not have an object, then also delete, also it can delete based on an "active" flag for an object.. Something I added to most of my models. You have to pass it the objects you want to check, the path to the objects files, the file field and a flag to delete inactive objects:

使用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."

This is how you can use this:

这就是你如何使用它:

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

#10


0  

make sure you write "self" before the file. so example above should be

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

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

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

I've forgotten the "self" before my file and that didn't work as it was looking in the global namespace.

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

#11


0  

If you already have number of unused files in your project and want to delete them, you can use django utility django-unused-media

如果您的项目中已经有许多未使用的文件,并且想要删除它们,您可以使用django实用程序django unuse -media

#12


-1  

I may have a special case since I am using the upload_to option on my file field with dynamic directory names but the solution I found was to use os.rmdir.

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

In models:

在模型:

import os

...

class Some_Model(models.Model):
     save_path = models.CharField(max_length=50)
     ...
     def delete(self, *args,**kwargs):
          os.rmdir(os.path.join(settings.MEDIA_ROOT, self.save_path)
          super(Some_Model,self).delete(*args, **kwargs)