Używam 1.2.5 ze standardowym ImageField i używam wbudowanego zaplecza pamięci. Przesyłanie plików jest prawidłowe, ale kiedy usuwam wpis od administratora, rzeczywisty plik na serwerze nie jest usuwany.
Używam 1.2.5 ze standardowym ImageField i używam wbudowanego zaplecza pamięci. Przesyłanie plików jest prawidłowe, ale kiedy usuwam wpis od administratora, rzeczywisty plik na serwerze nie jest usuwany.
Odpowiedzi:
Możesz odebrać sygnał pre_delete
lub post_delete
(patrz komentarz @ toto_tico poniżej) i wywołać metodę delete () na obiekcie FileField, a więc (w 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
pole nie jest puste lub może (przynajmniej spróbować) usunąć cały katalog MEDIA_ROOT. Dotyczy to nawet ImageField(null=False)
pól.
post_delete
sygnału, ponieważ jest to bezpieczniejsze w przypadku niepowodzenia usuwania z jakiegokolwiek powodu. Wtedy ani model, ani plik nie zostaną usunięte, co zapewni spójność danych. Proszę mnie poprawić, jeśli moje zrozumienie post_delete
i pre_delete
sygnały są błędne.
Spróbuj django-cleanup
pip install django-cleanup
settings.py
INSTALLED_APPS = (
...
'django_cleanup', # should go after your apps
)
Rozwiązanie Django 1.5: używam post_delete z różnych powodów wewnętrznych dla mojej aplikacji.
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)
Umieściłem to na dole pliku models.py.
original_image
pole jest ImageField
w moim Photo
modelu.
NotImplementedError: This backend doesn't support absolute paths.
Możesz łatwo to naprawić, przekazując nazwę pola pliku storage.delete()
zamiast ścieżki do pola pliku. Na przykład zamień ostatnie dwa wiersze tej odpowiedzi na storage, name = photo.original_image.storage, photo.original_image.name
wtedy storage.delete(name)
.
mymodel.myimagefield.delete(save=False)
zamiast niego. docs.djangoproject.com/en/dev/ref/files/file/ ...
mymodel.myimagefield.delete(save=False)
na post_delete
? Innymi słowy, widzę, że mogę usunąć plik, ale czy możesz usunąć plik, gdy model z polem obrazu zostanie usunięty?
post_delete
tym celu instance.myimagefield.delete(save=False)
zwróć uwagę na użycie instance
.
Ten kod działa dobrze w Django 1.4 również z panelem administracyjnym.
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)
Ważne jest, aby uzyskać miejsce do przechowywania i ścieżkę przed usunięciem modelu, w przeciwnym razie ten ostatni pozostanie nieważny również po usunięciu.
delete
nie zawsze jest wywoływana po usunięciu wiersza, musisz użyć sygnałów.
Ci potrzeba , aby usunąć rzeczywisty plik na obu delete
i 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)
Możesz rozważyć użycie sygnału pre_delete lub post_delete:
https://docs.djangoproject.com/en/dev/topics/signals/
Oczywiście te same powody, dla których usunięto automatyczne usuwanie FileField, mają również zastosowanie tutaj. Jeśli usuniesz plik, do którego istnieją odniesienia w innym miejscu, będziesz mieć problemy.
W moim przypadku wydawało się to właściwe, ponieważ miałem dedykowany model File do zarządzania wszystkimi moimi plikami.
Uwaga: z jakiegoś powodu post_delete nie działa prawidłowo. Plik został usunięty, ale rekord bazy danych pozostał, co jest całkowitym przeciwieństwem tego, czego bym się spodziewał, nawet w przypadku wystąpienia błędów. pre_delete działa jednak dobrze.
post_delete
nie zadziała, bo file_field.delete()
domyślnie zapisuje model do db, spróbuj file_field.delete(False)
docs.djangoproject.com/en/1.3/ref/models/fields/…
Może jest trochę za późno. Ale najłatwiejszym sposobem jest użycie sygnału post_save. Wystarczy pamiętać, że sygnały są wykonywane nawet podczas procesu usuwania QuerySet, ale metoda [model] .delete () nie jest wykonywana podczas procesu usuwania QuerySet, więc nie jest to najlepsza opcja, aby ją zastąpić.
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)
"""--------------------------------------------------------------------------"""
rdzeń / sygnały.py
import os
def delete_image_slide(sender, **kwargs):
slide = kwargs.get('instance')
try:
os.remove(slide.image.path)
except:
pass
Ta funkcjonalność zostanie usunięta w Django 1.3, więc nie będę na niej polegać.
Możesz zmienić delete
metodę danego modelu, aby usunąć plik przed całkowitym usunięciem wpisu z bazy danych.
Edytować:
Oto szybki przykład.
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()
usunie plik, ale MyModel.objects.all().delete()
nie. Użyj sygnałów.
Korzystanie z post_delete jest z pewnością właściwą drogą. Czasami jednak coś może pójść nie tak, a pliki nie są usuwane. Oczywiście jest tak, że masz kilka starych plików, które nie zostały usunięte przed użyciem post_delete. Stworzyłem funkcję, która usuwa pliki dla obiektów w oparciu o to, czy plik, do którego obiekt się odwołuje, nie istnieje, a następnie usuwa obiekt, jeśli plik nie ma obiektu, a następnie usuwa, również może usuwać na podstawie flagi „aktywny” dla obiekt .. Coś, co dodałem do większości moich modeli. Musisz przekazać mu obiekty, które chcesz sprawdzić, ścieżkę do plików obiektów, pole pliku i flagę do usuwania nieaktywnych obiektów:
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."
Oto jak możesz tego użyć:
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
upewnij się, że przed plikiem wpisałeś „ siebie ”. więc przykład powyżej powinien być
def delete(self, *args, **kwargs):
self.somefile.delete()
super(MyModel, self).delete(*args, **kwargs)
Zapomniałem o "self" przed moim plikiem i to nie działało, ponieważ wyglądało w globalnej przestrzeni nazw.
Jeśli masz już wiele nieużywanych plików w swoim projekcie i chcesz je usunąć, możesz użyć narzędzia django django-unused-media
Nie ma potrzeby instalowania żadnych pakietów! Jest bardzo łatwy w obsłudze w Django 2 . Próbowałem następującego rozwiązania przy użyciu Django 2 i SFTP Storage (jednak myślę, że działałoby z dowolnymi magazynami)
Najpierw napisz Custom Managera . Więc jeśli chcesz mieć możliwość usuwania plików modelu przy użyciu objects
metod, musisz napisać i użyć [Custom Manager] [3] (do nadpisywania delete()
metody objects
):
class CustomManager(models.Manager):
def delete(self):
for obj in self.get_queryset():
obj.delete()
Teraz musisz usunąć, image
zanim usuniesz sam model i aby przypisać CustomManager
do modelu, musisz zainicjować objects
wewnątrz swojego modelu:
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()
Mogę mieć specjalny przypadek, ponieważ używam opcji upload_to w moim polu pliku z dynamicznymi nazwami katalogów, ale rozwiązaniem, które znalazłem, było użycie os.rmdir.
W modelach:
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)