1.2.5'i standart bir ImageField ile ve yerleşik depolama arka ucunu kullanıyorum. Dosyalar sorunsuz yükleniyor, ancak yöneticiden bir girişi kaldırdığımda, sunucudaki asıl dosya silinmiyor.
1.2.5'i standart bir ImageField ile ve yerleşik depolama arka ucunu kullanıyorum. Dosyalar sorunsuz yükleniyor, ancak yöneticiden bir girişi kaldırdığımda, sunucudaki asıl dosya silinmiyor.
Yanıtlar:
pre_delete
Veya post_delete
sinyalini alabilir (aşağıdaki @ toto_tico'nun açıklamasına bakın) ve FileField nesnesinde delete () yöntemini çağırabilirsiniz , böylece (models.py içinde):
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
Alanın boş olup olmadığını kontrol ettiğinizden emin olun veya (en azından deneyebilirsiniz) MEDIA_ROOT dizininin tamamını silebilirsiniz. Bu ImageField(null=False)
tarlalar için bile geçerlidir .
post_delete
sinyali kullanmanızı tavsiye ederim çünkü silme işleminin herhangi bir nedenle başarısız olması durumunda daha güvenlidir. O zaman ne model ne dosya ne de veriler tutarlı bir şekilde silinir. Anladığım post_delete
ve pre_delete
sinyallerim yanlışsa lütfen beni düzeltin .
Django-cleanup'ı deneyin
pip install django-cleanup
settings.py
INSTALLED_APPS = (
...
'django_cleanup', # should go after your apps
)
Django 1.5 çözümü: Post_delete'yi uygulamamın içinde olan çeşitli nedenlerle kullanıyorum.
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)
Bunu models.py dosyasının altına yapıştırdım.
original_image
alandır ImageField
benim de Photo
modelin.
NotImplementedError: This backend doesn't support absolute paths.
dosya alanınızın adını geçirerek kolayca düzeltebilirim storage.delete()
yerine dosya alanın yolunun. Örneğin, bu cevabın son iki satırını storage, name = photo.original_image.storage, photo.original_image.name
o zaman ile değiştirin storage.delete(name)
.
mymodel.myimagefield.delete(save=False)
bunun yerine kullanabilirsiniz . docs.djangoproject.com/en/dev/ref/files/file/…
mymodel.myimagefield.delete(save=False)
üzerinde post_delete
? Başka bir deyişle, dosyayı silebileceğimi görebiliyorum, ancak imaj alanına sahip bir model silindiğinde dosyayı silebilir misiniz?
post_delete
yapmanız instance.myimagefield.delete(save=False)
, kullanımına dikkat instance
.
Bu kod Django 1.4'te Yönetici paneliyle de iyi çalışır.
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)
Modeli silmeden önce depolamayı ve yolu almak önemlidir, aksi takdirde ikincisi silinirse de geçersiz kalacaktır.
delete
bir satır silindiğinde her zaman çağrılmaz, sinyalleri kullanmanız gerekir.
Sen lüzum hem gerçek dosyayı kaldırmak için delete
ve 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 veya post_delete sinyali kullanmayı düşünebilirsiniz:
https://docs.djangoproject.com/en/dev/topics/signals/
Elbette, FileField otomatik silmesinin kaldırılmasının aynı nedenleri burada da geçerlidir. Başka bir yerde referans verilen bir dosyayı silerseniz sorun yaşarsınız.
Benim durumumda bu uygun görünüyordu çünkü tüm dosyalarımı yönetmek için özel bir Dosya modelim vardı.
Not: Bazı nedenlerden dolayı post_delete doğru çalışmıyor. Dosya silindi, ancak veritabanı kaydı kaldı; bu, hata koşullarında bile beklediğimin tam tersi. pre_delete yine de iyi çalışıyor.
post_delete
çalışmaz, çünkü file_field.delete()
varsayılan olarak modeli file_field.delete(False)
db'ye
Belki biraz geç kalmıştır. Ama benim için en kolay yol post_save sinyali kullanmaktır. Bir QuerySet silme işlemi sırasında bile sinyallerin çalıştırıldığını, ancak QuerySet silme işlemi sırasında [model] .delete () yönteminin çalıştırılmadığını hatırlamak için, bu nedenle onu geçersiz kılmak için en iyi seçenek bu değildir.
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
Bu işlevsellik Django 1.3'te kaldırılacak, böylece ona güvenmeyeceğim.
delete
Girişi veritabanından tamamen kaldırmadan önce dosyayı silmek için söz konusu modelin yöntemini geçersiz kılabilirsiniz .
Düzenle:
İşte hızlı bir örnek.
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()
dosyayı silerken silinmez MyModel.objects.all().delete()
. Sinyalleri kullanın.
Post_delete kullanmak kesinlikle doğru yoldur. Bazen işler ters gidebilir ve dosyalar silinmez. Elbette post_delete kullanılmadan önce silinmemiş olan bir sürü eski dosyanız olması da söz konusu. Nesnelerin başvurduğu dosyanın bulunup bulunmadığına bağlı olarak nesnelerin dosyalarını silen bir işlev oluşturdum, sonra nesneyi silip, dosyanın bir nesnesi yoksa, sonra da silebilir, ayrıca bir nesne .. Modellerimin çoğuna eklediğim bir şey. Kontrol etmek istediğiniz nesneleri, nesne dosyalarının yolunu, dosya alanını ve etkin olmayan nesneleri silmek için bir bayrak iletmelisiniz:
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."
Bunu şu şekilde kullanabilirsiniz:
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
Projenizde halihazırda kullanılmayan dosya sayısı varsa ve bunları silmek istiyorsanız, django yardımcı programını kullanabilirsiniz django-unused-media
Herhangi bir paket kurmanıza gerek yok! Django 2'de kullanımı çok kolaydır . Django 2 ve SFTP Depolamasını kullanarak aşağıdaki çözümü denedim (ancak herhangi bir depo ile çalışacağını düşünüyorum)
Önce bir Özel Yönetici yazın . Dolayısıyla objects
, yöntemleri kullanarak bir modelin dosyalarını silebilmek istiyorsanız, bir [Özel Yönetici] [3] yazmalı ve kullanmalısınız (geçersiz kılma delete()
yöntemi için objects
):
class CustomManager(models.Manager):
def delete(self):
for obj in self.get_queryset():
obj.delete()
Artık image
modelin kendisini silmeden önce silmelisiniz ve modele atamak CustomManager
için, modelinizin objects
içinde baş harfleri almanız gerekir :
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()
Dosya alanımda upload_to seçeneğini dinamik dizin isimleriyle kullandığım için özel bir durumum olabilir, ancak bulduğum çözüm os.rmdir kullanmaktı.
Modellerde:
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)