Estou usando 1.2.5 com um ImageField padrão e usando o back-end de armazenamento embutido. Os arquivos são carregados sem problemas, mas quando removo uma entrada do administrador, o arquivo real no servidor não é excluído.
django
file
storage
django-models
imagefield
Narkeeso
fonte
fonte
Respostas:
Você pode receber o sinal
pre_delete
oupost_delete
(veja o comentário de @toto_tico abaixo) e chamar o método delete () no objeto FileField, assim (em 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)
fonte
instance.file
campo não está vazio ou pode (pelo menos tentar) excluir todo o diretório MEDIA_ROOT. Isso se aplica até mesmo aosImageField(null=False)
campos.post_delete
sinal porque é mais seguro caso o delete falhe por algum motivo. Então, nem o modelo, nem o arquivo seriam excluídos mantendo os dados consistentes. Por favor, corrija-me se minha compreensãopost_delete
epre_delete
sinais estiverem errados.Experimente django-cleanup
pip install django-cleanup
settings.py
INSTALLED_APPS = ( ... 'django_cleanup', # should go after your apps )
fonte
Solução Django 1.5: Eu uso post_delete por vários motivos internos ao meu aplicativo.
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)
Coloquei isso na parte inferior do arquivo models.py.
o
original_image
campo é oImageField
no meuPhoto
modelo.fonte
NotImplementedError: This backend doesn't support absolute paths.
Você pode corrigir isso facilmente passando o nome do campo do arquivo para emstorage.delete()
vez do caminho do campo do arquivo. Por exemplo, substitua as duas últimas linhas desta resposta porstorage, name = photo.original_image.storage, photo.original_image.name
entãostorage.delete(name)
.mymodel.myimagefield.delete(save=False)
. docs.djangoproject.com/en/dev/ref/files/file/…mymodel.myimagefield.delete(save=False)
nopost_delete
? Em outras palavras, posso ver que posso excluir o arquivo, mas você pode excluir o arquivo quando um modelo que tem o imagefield é excluído?post_delete
vocêinstance.myimagefield.delete(save=False)
, observe o uso deinstance
.Este código funciona bem no Django 1.4 também com o painel de administração.
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)
É importante obter o armazenamento e o caminho antes de excluir o modelo, ou o último persistirá vazio também se excluído.
fonte
delete
nem sempre é chamado quando uma linha é excluída, você deve usar sinais.Você precisa remover o arquivo real em
delete
eupdate
.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)
fonte
Você pode considerar o uso de um sinal pre_delete ou post_delete:
https://docs.djangoproject.com/en/dev/topics/signals/
Obviamente, os mesmos motivos pelos quais a exclusão automática do FileField foi removida também se aplicam aqui. Se você deletar um arquivo referenciado em outro lugar, terá problemas.
No meu caso, isso parecia apropriado porque eu tinha um modelo de arquivo dedicado para gerenciar todos os meus arquivos.
Nota: Por algum motivo, post_delete não parece funcionar direito. O arquivo foi excluído, mas o registro do banco de dados permaneceu, o que é completamente o oposto do que eu esperava, mesmo em condições de erro. pre_delete funciona bem.
fonte
post_delete
não funcionará, porquefile_field.delete()
por padrão salva o modelo no banco de dados, tentefile_field.delete(False)
docs.djangoproject.com/en/1.3/ref/models/fields/…Talvez seja um pouco tarde. Mas a maneira mais fácil para mim é usar um sinal post_save. Apenas para lembrar que os sinais são executados mesmo durante um processo de exclusão de QuerySet, mas o método [model] .delete () não é executado durante o processo de exclusão de QuerySet, portanto, não é a melhor opção para substituí-lo.
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 / signs.py
import os def delete_image_slide(sender, **kwargs): slide = kwargs.get('instance') try: os.remove(slide.image.path) except: pass
fonte
Essa funcionalidade será removida no Django 1.3, então eu não confiaria nela.
Você pode substituir o
delete
método do modelo em questão para excluir o arquivo antes de remover completamente a entrada do banco de dados.Editar:
Aqui está um exemplo rápido.
class MyModel(models.Model): self.somefile = models.FileField(...) def delete(self, *args, **kwargs): somefile.delete() super(MyModel, self).delete(*args, **kwargs)
fonte
MyModel.objects.all()[0].delete()
irá deletar o arquivo enquantoMyModel.objects.all().delete()
não. Use sinais.Usar o post_delete é com certeza o caminho certo a seguir. Às vezes, embora as coisas possam dar errado e os arquivos não sejam excluídos. É claro que você tem um monte de arquivos antigos que não foram excluídos antes de post_delete ser usado. Eu criei uma função que exclui arquivos para objetos com base em se o arquivo ao qual o objeto faz referência não existe, exclua o objeto, se o arquivo não tiver um objeto, exclua também, também pode excluir com base em um sinalizador "ativo" para um objeto .. Algo que adicionei à maioria dos meus modelos. Você deve passar para ele os objetos que deseja verificar, o caminho para os arquivos de objetos, o campo do arquivo e um sinalizador para excluir objetos inativos:
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."
É assim que você pode usar:
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
fonte
certifique-se de escrever " self " antes do arquivo. então o exemplo acima deve ser
def delete(self, *args, **kwargs): self.somefile.delete() super(MyModel, self).delete(*args, **kwargs)
Esqueci o "self" antes do meu arquivo e isso não funcionou porque estava procurando no namespace global.
fonte
Se você já tem vários arquivos não usados em seu projeto e deseja excluí-los, você pode usar o utilitário django django-unused-media
fonte
Solução Django 2.x:
Não há necessidade de instalar nenhum pacote! É muito fácil de manusear no Django 2 . Tentei seguir a solução usando Django 2 e SFTP Storage (no entanto, acho que funcionaria com qualquer armazenamento)
Primeiro escreva um gerenciador personalizado . Portanto, se você deseja excluir arquivos de um modelo usando
objects
métodos, deve escrever e usar um [Custom Manager] [3] (para substituir odelete()
método deobjects
):class CustomManager(models.Manager): def delete(self): for obj in self.get_queryset(): obj.delete()
Agora você deve excluir
image
antes de excluir o próprio modelo e para atribuir oCustomManager
ao modelo, você deve inicializarobjects
dentro de seu modelo:class MyModel(models.Model): image = models.ImageField(upload_to='/pictures/', blank=True) objects = CustomManager() # add CustomManager to model def delete(self, using=None, keep_parents=False): objects = CustomManager() # just add this line of code inside of your model def delete(self, using=None, keep_parents=False): self.image.storage.delete(self.song.name) super().delete()
fonte
Posso ter um caso especial, pois estou usando a opção upload_to em meu campo de arquivo com nomes de diretório dinâmico, mas a solução que encontrei foi usar os.rmdir.
Nos modelos:
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)
fonte