Tôi đang sử dụng 1.2.5 với ImageField tiêu chuẩn và sử dụng phần phụ trợ lưu trữ tích hợp sẵn. Tệp tải lên tốt nhưng khi tôi xóa mục nhập từ quản trị viên, tệp thực tế trên máy chủ không xóa.
Tôi đang sử dụng 1.2.5 với ImageField tiêu chuẩn và sử dụng phần phụ trợ lưu trữ tích hợp sẵn. Tệp tải lên tốt nhưng khi tôi xóa mục nhập từ quản trị viên, tệp thực tế trên máy chủ không xóa.
Câu trả lời:
Bạn có thể nhận pre_delete
hoặc post_delete
signal (xem nhận xét của @ toto_tico bên dưới) và gọi phương thức delete () trên đối tượng FileField, do đó (trong 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
trường không trống hoặc nó có thể (ít nhất là thử) xóa toàn bộ thư mục MEDIA_ROOT. Điều này áp dụng ngay cả cho ImageField(null=False)
các trường.
post_delete
tín hiệu vì nó an toàn hơn trong trường hợp xóa không thành công vì bất kỳ lý do gì. Sau đó, cả mô hình, tệp tin sẽ không bị xóa để giữ cho dữ liệu nhất quán. Vui lòng sửa cho tôi nếu sự hiểu biết post_delete
và pre_delete
tín hiệu của tôi là sai.
Thử django-cleanup
pip install django-cleanup
settings.py
INSTALLED_APPS = (
...
'django_cleanup', # should go after your apps
)
Giải pháp Django 1.5: Tôi sử dụng post_delete vì nhiều lý do nội bộ ứng dụng của tôi.
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)
Tôi đã mắc kẹt cái này ở cuối tệp models.py.
các original_image
lĩnh vực là ImageField
trong tôi Photo
mô hình.
NotImplementedError: This backend doesn't support absolute paths.
Bạn có thể dễ dàng sửa lỗi này bằng cách chuyển tên của trường tệp storage.delete()
thay vì đường dẫn của trường tệp. Ví dụ: thay thế hai dòng cuối cùng của câu trả lời này bằng storage, name = photo.original_image.storage, photo.original_image.name
sau đó storage.delete(name)
.
mymodel.myimagefield.delete(save=False)
thay thế. docs.djangoproject.com/en/dev/ref/files/file/…
mymodel.myimagefield.delete(save=False)
trên post_delete
không? Nói cách khác, tôi có thể thấy rằng tôi có thể xóa tệp, nhưng bạn có thể xóa tệp khi mô hình có trường hình ảnh bị xóa không?
post_delete
bạn làm instance.myimagefield.delete(save=False)
, lưu ý việc sử dụng instance
.
Mã này chạy tốt trên Django 1.4 cũng với bảng điều khiển Quản trị.
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)
Điều quan trọng là phải lấy được bộ nhớ và đường dẫn trước khi xóa mô hình, nếu không mô hình sau đó cũng sẽ vô hiệu nếu bị xóa.
delete
không phải lúc nào cũng được gọi khi một hàng bị xóa, bạn phải sử dụng các tín hiệu.
Bạn cần xóa tệp thực trên cả delete
và 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)
Bạn có thể cân nhắc sử dụng tín hiệu pre_delete hoặc post_delete:
https://docs.djangoproject.com/en/dev/topics/signals/
Tất nhiên, các lý do tương tự khiến tính năng xóa tự động FileField bị xóa cũng được áp dụng ở đây. Nếu bạn xóa một tệp được tham chiếu ở một nơi khác, bạn sẽ gặp sự cố.
Trong trường hợp của tôi, điều này có vẻ phù hợp vì tôi có một mô hình Tệp chuyên dụng để quản lý tất cả các tệp của mình.
Lưu ý: Vì một số lý do post_delete dường như không hoạt động bình thường. Tệp đã bị xóa, nhưng bản ghi cơ sở dữ liệu vẫn còn, điều này hoàn toàn trái ngược với những gì tôi mong đợi, ngay cả trong các điều kiện lỗi. pre_delete hoạt động tốt mặc dù.
post_delete
sẽ không làm việc, vì file_field.delete()
theo mặc định tiết kiệm mô hình để db, hãy thử file_field.delete(False)
docs.djangoproject.com/en/1.3/ref/models/fields/...
Có lẽ hơi muộn. Nhưng cách dễ nhất đối với tôi là sử dụng tín hiệu post_save. Chỉ cần nhớ rằng các tín hiệu được xuất phát ngay cả trong quá trình xóa QuerySet, nhưng phương thức [model] .delete () không được cấp trong quá trình xóa QuerySet, vì vậy, nó không phải là tùy chọn tốt nhất để ghi đè nó.
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
Chức năng này sẽ bị loại bỏ trong Django 1.3 nên tôi sẽ không dựa vào nó.
Bạn có thể ghi đè delete
phương thức của mô hình được đề cập để xóa tệp trước khi xóa hoàn toàn mục nhập khỏi cơ sở dữ liệu.
Biên tập:
Đây là một ví dụ nhanh.
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()
sẽ xóa tệp trong khi MyModel.objects.all().delete()
sẽ không. Sử dụng các tín hiệu.
Sử dụng post_delete chắc chắn là cách đi đúng đắn. Đôi khi, mặc dù mọi thứ có thể sai và các tệp không bị xóa. Tất nhiên có trường hợp bạn có một loạt các tệp cũ chưa bị xóa trước khi post_delete được sử dụng. Tôi đã tạo một chức năng xóa tệp cho các đối tượng dựa trên nếu tệp mà đối tượng tham chiếu không tồn tại thì xóa đối tượng, nếu tệp không có đối tượng, thì cũng xóa, cũng có thể xóa dựa trên cờ "hoạt động" cho một đối tượng .. Một cái gì đó tôi đã thêm vào hầu hết các mô hình của mình. Bạn phải chuyển cho nó các đối tượng bạn muốn kiểm tra, đường dẫn đến tệp đối tượng, trường tệp và cờ để xóa các đối tượng không hoạt động:
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."
Đây là cách bạn có thể sử dụng:
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
đảm bảo rằng bạn viết " self " trước tệp. vì vậy ví dụ trên phải là
def delete(self, *args, **kwargs):
self.somefile.delete()
super(MyModel, self).delete(*args, **kwargs)
Tôi đã quên "bản thân" trước tệp của mình và điều đó không hoạt động khi nó đang tìm kiếm trong không gian tên chung.
Nếu bạn đã có một số tệp không sử dụng trong dự án của mình và muốn xóa chúng, bạn có thể sử dụng tiện ích django django-used-media
Không cần cài đặt bất kỳ gói nào! Nó rất dễ dàng để xử lý trong Django 2 . Tôi đã thử giải pháp sau bằng cách sử dụng Django 2 và SFTP Storage (tuy nhiên tôi nghĩ nó sẽ hoạt động với bất kỳ bộ lưu trữ nào)
Đầu tiên hãy viết Trình quản lý tùy chỉnh . Vì vậy, nếu bạn muốn có thể xóa các tệp của một mô hình bằng objects
các phương thức, bạn phải viết và sử dụng [Trình quản lý tùy chỉnh] [3] (đối với delete()
phương pháp ghi đè của objects
):
class CustomManager(models.Manager):
def delete(self):
for obj in self.get_queryset():
obj.delete()
Bây giờ bạn phải xóa image
trước khi xóa xóa chính mô hình và để gán CustomManager
mô hình đó, bạn phải ký tắt objects
bên trong mô hình của mình:
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()
Tôi có thể gặp trường hợp đặc biệt vì tôi đang sử dụng tùy chọn upload_to trên trường tệp của mình với các tên thư mục động nhưng giải pháp tôi tìm thấy là sử dụng os.rmdir.
Trong các mô hình:
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)