Как получить Django Admin для удаления файлов при удалении объекта из базы данных / модели?
Я использую 1.2.5 со стандартным полем ImageField и использую встроенный сервер хранения. Файлы загружаются нормально, но когда я удаляю запись из admin, фактический файл на сервере не удаляется.
12 ответов
вы можете получить pre_delete
или post_delete
signal (см. комментарий @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 файл.
на
попробовать Джанго-очистка
pip install django-cleanup
settings.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)
важно получить хранилище и путь перед удалением модели, или последняя будет сохраняться void также при удалении.
вы нужно удалить сам файл как 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)