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!
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
.
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)
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.file
comme nom de champ, ce qui n’est pas un bon style car il se heurte à l’identificateur d’objet file
intégré.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éthodeFileField
’sdelete()
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).
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
)
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)
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()
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)
@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
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='')