Я использую 1.2.5 со стандартным ImageField и использую встроенную систему хранения. Файлы загружаются нормально, но когда я удаляю запись от администратора, фактический файл на сервере не удаляется.
Я использую 1.2.5 со стандартным ImageField и использую встроенную систему хранения. Файлы загружаются нормально, но когда я удаляю запись от администратора, фактический файл на сервере не удаляется.
Ответы:
Вы можете получить сигнал 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)
instance.file
поле не пустое или оно может (по крайней мере, попытаться) удалить весь каталог MEDIA_ROOT. Это касается даже ImageField(null=False)
полей.
post_delete
сигнал, потому что это безопаснее в случае, если удаление не удастся по какой-либо причине. Тогда ни модель, ни файл не будут удалены, сохраняя согласованность данных. Пожалуйста, поправьте меня, если я неправильно понимаю post_delete
и pre_delete
сигналы.
Попробуйте django-cleanup
pip install django-cleanup
settings.py
INSTALLED_APPS = (
...
'django_cleanup', # should go after your apps
)
Решение 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.
original_image
поле является ImageField
в моей Photo
модели.
NotImplementedError: This backend doesn't support absolute paths.
Вы можете легко исправить это, передав имя поля файла storage.delete()
вместо пути к полю файла. Например, замените последние две строки этого ответа на storage, name = photo.original_image.storage, photo.original_image.name
затем storage.delete(name)
.
mymodel.myimagefield.delete(save=False)
вместо него. docs.djangoproject.com/en/dev/ref/files/file/…
mymodel.myimagefield.delete(save=False)
на post_delete
? Другими словами, я вижу, что могу удалить файл, но можете ли вы удалить файл, когда удаляется модель, имеющая поле изображения?
post_delete
вас instance.myimagefield.delete(save=False)
, обратите внимание на использование instance
.
Этот код хорошо работает на 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
не всегда вызывается при удалении строки, необходимо использовать сигналы.
Вам необходимо удалить фактический файл как на, так 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_delete
, не сработает, потому что file_field.delete()
по умолчанию модель сохраняет в db, попробуйте file_field.delete(False)
docs.djangoproject.com/en/1.3/ref/models/fields/…
Может, уже немного поздно. Но самый простой способ для меня - использовать сигнал post_save. Просто помните, что сигналы исключаются даже во время процесса удаления QuerySet, но метод [model] .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)
"""--------------------------------------------------------------------------"""
ядро / сигналы.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
метод рассматриваемой модели, чтобы удалить файл перед полным удалением записи из базы данных.
Редактировать:
Вот небольшой пример.
class MyModel(models.Model):
self.somefile = models.FileField(...)
def delete(self, *args, **kwargs):
somefile.delete()
super(MyModel, self).delete(*args, **kwargs)
MyModel.objects.all()[0].delete()
удалит файл, пока MyModel.objects.all().delete()
не будет. Используйте сигналы.
Использование 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
Никаких пакетов устанавливать не нужно! В Django 2 это очень просто . Я пробовал следующее решение с использованием Django 2 и SFTP Storage (однако я думаю, что оно будет работать с любыми хранилищами)
Сначала напишите Custom Manager . Поэтому, если вы хотите иметь возможность удалять файлы модели с помощью objects
методов, вы должны написать и использовать [Custom Manager] [3] (для delete()
метода переопределения objects
):
class CustomManager(models.Manager):
def delete(self):
for obj in self.get_queryset():
obj.delete()
Теперь вы должны удалить image
перед удалением, удалив саму модель и для назначения CustomManager
модели, вы должны инициализировать objects
внутри своей модели:
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()
У меня может быть особый случай, поскольку я использую опцию upload_to в своем поле файла с динамическими именами каталогов, но решение, которое я нашел, заключалось в использовании os.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)