web-dev-qa-db-fra.com

Django supprimer FileField

Je construis une application web à Django. J'ai un modèle qui télécharge un fichier, mais je ne peux pas le supprimer. Voici mon code:

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)

Ensuite, dans "python manage.py Shell", je fais ceci:

song = Song.objects.get(pk=1)
song.delete()

Il supprime de la base de données mais pas le fichier sur le serveur . Que puis-je essayer d'autre?

Merci!

57
Marcos Aguayo

Avant Django 1.3, le fichier était automatiquement supprimé du système de fichiers lorsque vous supprimiez l'instance de modèle correspondante. Vous utilisez probablement une version plus récente de Django, vous devrez donc implémenter vous-même la suppression du fichier du système de fichiers.

Vous pouvez le faire de plusieurs manières, l'une utilisant un signal pre_delete ou post_delete

Exemple

Ma méthode de choix actuelle consiste à combiner les signaux post_delete et pre_save, ce qui permet de supprimer les fichiers obsolètes lorsque les modèles correspondants sont supprimés ou que leurs fichiers sont modifiés.

Basé sur un modèle hypothétique 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)
  • Cas suivant: si votre application télécharge un nouveau fichier et pointe l'instance du modèle dans le nouveau fichier sans appeler save() (par exemple, en mettant à jour en masse une QuerySet), l'ancien fichier restera en attente car les signaux ne seront pas exécutés. Cela ne se produit pas si vous utilisez des méthodes de traitement de fichiers conventionnelles.
  • Je pense que l’une des applications que j’ai construite a ce code en production mais qu’il utilise néanmoins à vos risques et périls.
  • Style de codage: cet exemple utilise file comme nom de champ, ce qui n’est pas un bon style car il se heurte à l’identificateur d’objet file intégré.

Voir également

  • FieldFile.delete() dans la référence de champ du modèle Django 1.11 (notez qu'il décrit la classe FieldFile, mais vous appelez .delete() directement sur le champ: instance de FileField vers l'instance correspondante de FieldFile et vous accédez à ses méthodes )

    Notez que lorsqu'un modèle est supprimé, les fichiers associés ne sont pas supprimés. Si vous devez nettoyer des fichiers orphelins, vous devez les gérer vous-même (par exemple, avec une commande de gestion personnalisée pouvant être exécutée manuellement ou programmée pour être exécutée périodiquement via cron, par exemple).

  • Pourquoi Django ne supprime pas automatiquement les fichiers: entrée dans les notes de publication de Django 1.3

    Dans les versions précédentes de Django, lorsqu'une instance de modèle contenant une FileField était supprimée, FileField se chargeait également de supprimer le fichier du stockage principal. Cela a ouvert la porte à plusieurs scénarios de perte de données, notamment des transactions annulées et des champs sur différents modèles faisant référence au même fichier. Dans Django 1.3, lorsqu’un modèle est supprimé, la méthode FileField ’s delete() ne sera pas appelée. Si vous avez besoin de nettoyer les fichiers orphelins, vous devrez le gérer vous-même (par exemple, avec une commande de gestion personnalisée pouvant être exécutée manuellement ou programmée pour être exécutée périodiquement via cron, par exemple).

  • Exemple d'utilisation d'un signal pre_delete uniquement

103
Tony

Essayez Django-cleanup , il appelle automatiquement la méthode delete sur FileField lorsque vous supprimez le modèle.

pip install Django-cleanup

settings.py

INSTALLED_APPS = (
     ...
    'Django_cleanup', # should go after your apps
)
29
un1t

Vous pouvez également simplement écraser la fonction de suppression du modèle pour vérifier si le fichier existe et le supprimer avant d'appeler la super fonction.

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)
13
Shashank Singla

Vous pouvez supprimer un fichier du système de fichiers en appelant la méthode .delete du champ de fichier indiquée ci-dessous avec Django> = 1.10:

obj = Song.objects.get(pk=1)
obj.song.delete()
8
Mesut Tasci

Voici une application qui supprimera les anciens fichiers chaque fois que le modèle est supprimé ou qu'un nouveau fichier est chargé: 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)
3
lehins

@Anton Strogonoff

Il me manque quelque chose dans le code lorsqu'un fichier est modifié. Si vous créez un nouveau fichier, une erreur est générée, car il s'agit d'un nouveau fichier qui n'a pas trouvé de chemin. J'ai modifié le code de fonction et ajouté une phrase try/except et cela fonctionne bien. 

@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
3
Java Developer

Ce code sera exécuté chaque fois que je téléchargerai une nouvelle image (champ du logo) et vérifierai si un logo existe déjà, fermez-le et supprimez-le du disque. La même procédure pourrait bien sûr être faite dans la fonction de récepteur. J'espère que cela t'aides.

 #  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='')
0
LanfeaR