ฉันใช้ 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
สัญญาณของฉันไม่ถูกต้อง
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()
โดยค่าเริ่มต้นจะบันทึกโมเดลลงในฐานข้อมูลลองfile_field.delete(False)
docs.djangoproject.com/th/1.3/ref/models/fields/…
อาจจะสายไปหน่อย แต่วิธีที่ง่ายที่สุดสำหรับฉันคือใช้สัญญาณ post_save เพียงจำไว้ว่าสัญญาณจะถูกลบออกแม้ในระหว่างกระบวนการลบ QuerySet แต่เมธอด [model] .delete () จะไม่ถูกลบออกในระหว่างกระบวนการลบ QuerySet ดังนั้นจึงไม่ใช่ตัวเลือกที่ดีที่สุดในการแทนที่
หลัก / 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
ฟังก์ชันนี้จะถูกลบออกใน 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 22 ฉันได้ลองใช้วิธีแก้ปัญหาต่อไปนี้โดยใช้ Django 2 และ SFTP Storage (แต่ฉันคิดว่ามันจะใช้ได้กับที่เก็บข้อมูลใด ๆ )
ก่อนอื่นให้เขียนไฟล์ ผู้จัดการที่กำหนดเอง ดังนั้นหากคุณต้องการลบไฟล์ของโมเดลโดยใช้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)