標準のImageFieldで1.2.5を使用し、組み込みのストレージバックエンドを使用しています。ファイルは正常にアップロードされますが、管理者からエントリを削除しても、サーバー上の実際のファイルは削除されません。
回答:
pre_delete
orpost_delete
シグナルを受信し(以下の@toto_ticoのコメントを参照)、FileFieldオブジェクトでdelete()メソッドを呼び出すことができます(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()
代わりに、ファイルフィールドのパスの。たとえば、この回答の最後の2行をstorage, name = photo.original_image.storage, photo.original_image.name
thenに置き換え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
。
このコードは、管理パネルを備えたDjango1.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
ため、おそらく機能しません。docs.djangoproject.com/ en / 1.3 / ref / models / fieldsfile_field.delete()
file_field.delete(False)
多分それは少し遅いです。しかし、私にとって最も簡単な方法は、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)
"""--------------------------------------------------------------------------"""
core / signal.py
import os
def delete_image_slide(sender, **kwargs):
slide = kwargs.get('instance')
try:
os.remove(slide.image.path)
except:
pass
この機能はDjango1.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
プロジェクトに未使用のファイルがすでに多数あり、それらを削除したい場合は、djangoユーティリティdjango-unused-mediaを使用できます。
パッケージをインストールする必要はありません!Django2での処理は非常に簡単ですます。Django2とSFTPストレージを使用して次のソリューションを試しました(ただし、どのストレージでも機能すると思います)
まず、カスタムマネージャーを作成します。したがって、objects
メソッドを使用してモデルのファイルを削除できるようにする場合は、[カスタムマネージャー] [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)