Je crée une application Web dans 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!
django
django-models
Marcos Aguayo
la source
la source
Réponses:
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 la suppression du fichier du système de fichiers vous-même.
Vous pouvez le faire de plusieurs manières, l'une d'elles utilisant un
pre_delete
oupost_delete
signal .Exemple
Ma méthode de choix est actuellement un mélange de
post_delete
etpre_save
signaux, ce qui permet de supprimer les fichiers obsolètes chaque fois que les modèles correspondants sont supprimés ou que leurs fichiers sont modifiés.Basé sur un
MediaFile
modèle hypothétique :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)
save()
(par exemple en mettant à jour en masse aQuerySet
), l'ancien fichier continuera à traîner car les signaux ne seront pas exécutés. Cela ne se produit pas si vous utilisez des méthodes de gestion de fichiers conventionnelles.file
comme nom de champ, ce qui n'est pas un bon style car il entre en conflit avec l'file
identifiant d'objet intégré .Voir également
FieldFile.delete()
dans la référence de champ de modèle Django 1.11 (notez qu'elle décrit laFieldFile
classe, mais vous appelleriez.delete()
directement sur le champ:FileField
instance proxies vers l'FieldFile
instance correspondante , et vous accédez à ses méthodes comme s'il s'agissait de champs)Pourquoi Django ne supprime pas automatiquement les fichiers: entrée dans les notes de publication de Django 1.3
Exemple d'utilisation d'un
pre_delete
signal uniquementla source
instance.song.delete(save=False)
, car il utilise le bon moteur de stockage django.os.path.isfile(old_file.path)
échoue car elleold_file.path
génère une erreur (aucun fichier n'est associé au champ). Je l'ai corrigé en ajoutantif old_file:
juste avant l'appel àos.path.isfile()
.Essayez django-cleanup , il invoque automatiquement la méthode de suppression sur FileField lorsque vous supprimez le modèle.
pip install django-cleanup
settings.py
INSTALLED_APPS = ( ... 'django_cleanup', # should go after your apps )
la source
Vous pouvez supprimer le fichier du système de fichiers avec la
.delete
méthode d' appel du champ de fichier ci-dessous avec Django> = 1.10:obj = Song.objects.get(pk=1) obj.song.delete()
la source
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)
la source
queryset.delete()
ne nettoiera pas les fichiers avec cette solution. Vous devrez parcourir l'ensemble de requêtes et appeler.delete()
chaque objet.Solution Django 2.x:
Il est très facile de gérer la suppression de fichiers dans Django 2 . J'ai essayé la solution suivante en utilisant Django 2 et le stockage SFTP ainsi que le STOCKAGE FTP, et je suis à peu près sûr que cela fonctionnera avec tous les autres gestionnaires de stockage qui ont implémenté la
delete
méthode. (ladelete
méthode est l'une desstorage
méthodes abstraites.)Remplacez la
delete
méthode du modèle de manière à ce que l'instance supprime ses FileFields avant de se supprimer: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.song.name) super().delete()
Cela fonctionne assez facilement pour moi. Si vous souhaitez vérifier si le fichier existe avant la suppression, vous pouvez utiliser
storage.exists
. par exempleself.song.storage.exists(self.song.name)
retournera unboolean
représentant si la chanson existe. Cela ressemblera donc à ceci:def delete(self, using=None, keep_parents=False): # assuming that you use same storage for all files in this model: storage = self.song.storage if storage.exists(self.song.name): storage.delete(self.song.name) if storage.exists(self.image.name): storage.delete(self.song.name) super().delete()
EDIT (en plus):
Comme @HeyMan l'a mentionné, avec cette solution, l'appel
Song.objects.all().delete()
ne supprime pas les fichiers! Cela se produit carSong.objects.all().delete()
exécute la requête de suppression de Default Manager . Donc, si vous voulez pouvoir supprimer les fichiers d'un modèle à l'aide deobjects
méthodes, vous devez écrire et utiliser un gestionnaire personnalisé (juste pour remplacer sa requête de suppression):class CustomManager(models.Manager): def delete(self): for obj in self.get_queryset(): obj.delete()
et pour affecter le
CustomManager
au modèle, vous devez initialiserobjects
à l' intérieur de votre modèle: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.song.name) super().delete()
Vous pouvez maintenant utiliser
.delete()
à la fin de toutes lesobjects
sous-requêtes. J'ai écrit le plus simpleCustomManager
, mais vous pouvez le faire mieux en renvoyant quelque chose sur les objets que vous avez supprimés ou ce que vous voulez.la source
Voici une application qui supprimera les anciens fichiers chaque fois qu'un modèle est supprimé ou qu'un nouveau fichier est télé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)
la source
@Anton Strogonoff
Il me manque quelque chose dans le code lors d'un changement de fichier, si vous créez un nouveau fichier génère une erreur, car il s'agit d'un nouveau fichier qui n'a pas trouvé de chemin. J'ai modifié le code de la 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
la source
try:
bloc, cependant (AttributeError
peut-être?).Ce code s'exécutera chaque fois que je télécharge une nouvelle image (champ de logo) et vérifie si un logo existe déjà, fermez-le et supprimez-le du disque. La même procédure pourrait bien sûr être effectuée en fonction du 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='')
la source