Comment faire pour que Django Admin supprime des fichiers lorsque je supprime un objet de la base de données/du modèle?

j'utilise 1.2.5 avec un champ D'image standard et j'utilise le backend de stockage intégré. Le téléchargement de fichiers amende, mais quand je supprime une entrée de l'administrateur le fichier réel sur le serveur ne supprime pas.

76
demandé sur narkeeso 2011-03-21 04:19:08

12 réponses

vous pouvez recevoir le signal pre_delete ou post_delete (voir le commentaire de @toto_tico ci-dessous) et appeler la méthode delete sur L'objet FileField, donc (en 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)
91
répondu darrinm 2017-12-09 07:11:26

Django 1.5 solution: j'utilise post_delete pour diverses raisons internes à mon application.

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)

j'ai collé ceci au fond de la models.py file.

le champ original_image est le champ ImageField de mon modèle Photo .

33
répondu Kushal 2013-07-26 23:54:11

Essayer django-nettoyage

pip install django-cleanup

settings.py

INSTALLED_APPS = (
    ...
    'django_cleanup', # should go after your apps
)
33
répondu un1t 2015-03-17 17:21:48

ce code fonctionne bien sur Django 1.4 également avec le panneau D'administration.

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)

il est important d'obtenir le stockage et le chemin avant de supprimer le modèle ou ce dernier persistera sans effet même s'il est supprimé.

17
répondu Davide Muzzarelli 2012-07-04 17:28:52

Vous besoin pour supprimer le fichier réel sur les deux delete et 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)
7
répondu un33k 2015-09-18 01:30:06

vous pouvez utiliser un signal pré_delete ou post_delete:

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

bien sûr, les mêmes raisons que FileField suppression automatique a été supprimé s'appliquent également ici. Si vous supprimez un fichier référencé ailleurs, vous aurez des problèmes.

dans mon cas cela a semblé approprié parce que j'avais un modèle de fichier dédié pour gérer tout mon fichier.

Note: pour une raison quelconque post_delete ne semble pas fonctionner correctement. Le fichier a été supprimé, mais l'enregistrement de la base de données est resté, ce qui est complètement le contraire de ce que je m'attendrais, même dans des conditions d'erreur. pre_delete fonctionne bien cependant.

6
répondu SystemParadox 2012-03-12 08:33:52

c'est Peut-être un peu tard. Mais la façon la plus simple pour moi est d'utiliser un post_save signal. Juste pour se rappeler que les signaux sont excécutés même pendant un processus de suppression de QuerySet, mais le [model].delete () method n'est pas excecuted pendant le processus de delete QuerySet, donc ce n'est pas la meilleure option pour le surcharger.

core/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

import os

def delete_image_slide(sender, **kwargs):
    slide = kwargs.get('instance')
    try:
        os.remove(slide.image.path)
    except:
        pass
3
répondu Mauricio 2013-08-16 02:26:07

cette fonctionnalité sera supprimée dans Django 1.3 donc je ne compterais pas dessus.

vous pouvez outrepasser la méthode delete du modèle en question pour supprimer le fichier avant de supprimer complètement l'entrée de la base de données.

Edit:

Voici un exemple rapide.

class MyModel(models.Model):

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

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

        super(MyModel, self).delete(*args, **kwargs)
1
répondu Derek Reynolds 2011-11-01 10:21:26

utiliser post_delete est certainement la bonne façon de faire. Parfois les choses peuvent mal tourner, et les fichiers ne sont pas supprimés. Il y a bien sûr le cas où vous avez un tas de vieux fichiers qui n'ont pas été supprimés avant post_delete a été utilisé. J'ai créé une fonction qui supprime des fichiers pour les objets basés sur si le fichier les références de l'objet n'existe pas puis supprimer l'objet, si le fichier n'a pas d'objet, puis supprimer aussi, il peut également supprimer basé sur un drapeau "actif" pour un objet.. Quelque chose que j'ai ajouté à la plupart de mes modèles. Vous devez passer les objets que vous souhaitez vérifier, le chemin d'accès aux fichiers objets, le champ de fichier et un drapeau pour supprimer des objets inactifs:

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

Voici comment vous pouvez utiliser ceci:

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
1
répondu radtek 2014-06-05 23:22:10

assurez-vous d'écrire " auto " avant de le fichier. ainsi, l'exemple ci-dessus devrait être

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

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

j'ai oublié le " moi " avant mon fichier et cela n'a pas fonctionné car il cherchait dans l'espace-Nom global.

0
répondu Bjorn 2013-03-27 17:15:01

Si vous avez déjà nombre de fichiers inutilisés dans votre projet et que vous voulez les supprimer, vous pouvez utiliser django utilitaire django-inutilisé-médias

0
répondu Andrey Kolpakov 2017-01-11 18:50:44

j'ai peut-être un cas particulier puisque j'utilise l'option upload_to sur mon champ de fichier avec des noms de répertoires dynamiques, mais la solution que j'ai trouvée était d'utiliser os.rmdir.

dans les modèles:

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
répondu carruthd 2013-02-17 23:01:55