12 ответов:
вы можете получить
pre_deleteилиpost_deleteсигнал (см. комментарий @toto_tico ниже) и вызовите метод delete на объекте FileField, таким образом (в 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)
Django 1.5 решение: я использую post_delete по различным причинам, которые являются внутренними для моего приложения.
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)Я застрял это в нижней части models.py файл.
the
попробовать django-cleanup
pip install django-cleanupsettings.py
INSTALLED_APPS = ( ... 'django_cleanup', # should go after your apps )
этот код хорошо работает на Django 1.4 также с панелью администратора.
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)важно получить хранилище и путь перед удалением модели или последняя будет сохраняться пустой также при удалении.
вы нужно удалить сам файл как
deleteи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)
вы можете рассмотреть возможность использования сигнала pre_delete или post_delete:
https://docs.djangoproject.com/en/dev/topics/signals/
конечно, те же причины, по которым было удалено автоматическое удаление FileField, также применяются здесь. Если вы удалите файл, на который ссылаются в другом месте, у вас возникнут проблемы.
в моем случае это казалось уместным, потому что у меня была выделенная файловая модель для управления всеми моими файлами.
примечание: по какой-то причине post_delete, кажется, не работает правильно. Файл был удален, но запись базы данных осталась, что полностью противоположно тому, что я ожидал бы, даже в условиях ошибки. pre_delete работает отлично, хотя.
может быть, уже немного поздно. Но самый простой способ для меня-использовать post_save сигнал. Просто помните, что сигналы исключаются даже во время процесса удаления QuerySet, но [модель].метод delete () не исключается во время процесса удаления QuerySet, поэтому это не лучший вариант для его переопределения.
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
эта функциональность будет удалена в Django 1.3, поэтому я бы не стал полагаться на нее.
вы можете переопределить
deleteметод рассматриваемой модели для удаления файла перед полным удалением записи из базы данных.Edit:
вот краткий пример.
class MyModel(models.Model): self.somefile = models.FileField(...) def delete(self, *args, **kwargs): somefile.delete() super(MyModel, self).delete(*args, **kwargs)
использование post_delete-это наверняка правильный путь. Хотя иногда все может пойти не так, и файлы не удаляются. Конечно, есть случай, когда у вас есть куча старых файлов, которые не были удалены до использования post_delete. Я создал функцию, которая удаляет файлы для объектов, основанных на том, что если файл, на который ссылается объект, не существует, то удалите объект, если в файле нет объекта, а затем удалите, также он может удалить на основе "активного" флага для объекта.. Что-то я добавил к большинству моих моделей. Вы должны передать ему объекты, которые вы хотите проверить, путь к файлам объектов, поле файла и флаг для удаления неактивных объектов:
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."Это, как вы можете использовать это:
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
убедитесь, что вы пишете " self" до файла. так что пример выше должен быть
def delete(self, *args, **kwargs): self.somefile.delete() super(MyModel, self).delete(*args, **kwargs)Я забыл "себя" перед моим файлом, и это не сработало, поскольку он искал в глобальном пространстве имен.
Если у вас уже есть количество неиспользуемых файлов в вашем проекте и вы хотите удалить их, вы можете использовать утилиту django django-unused-media
у меня может быть особый случай, так как я использую опцию upload_to в моем поле файла с динамическими именами каталогов, но решение, которое я нашел, было использовать ОС.команда rmdir.
модели:
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)
Comments