Pytanie Jak mogę uzyskać dostęp do Django Admin, gdy usuwam obiekt z bazy danych / modelu?


Używam wersji 1.2.5 ze standardowym ImageField i korzystam z wbudowanego zaplecza pamięci masowej. Przesyłanie plików jest w porządku, ale kiedy usuwam wpis z administratora, rzeczywisty plik na serwerze nie jest usuwany.


76
2018-03-21 01:19


pochodzenie


Hm, właściwie tak powinno być. Sprawdź uprawnienia do plików w swoim folderze przesyłania (zmień na 0777). - Torsten Engelbrecht
Django usunął funkcję automatycznego usuwania (dla Googlersów, którzy widzą powyższy komentarz). - Mark


Odpowiedzi:


Możesz otrzymać pre_delete lub post_delete signal (patrz komentarz @ toto_tico poniżej) i wywołaj 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)

90
2018-01-14 01:11



Pamiętaj, aby dodać czek, jeśli instance.file pole nie jest puste lub może (przynajmniej spróbować) usunąć cały katalog MEDIA_ROOT. Dotyczy to nawet ImageField(null=False) pola. - Antony Hatchkins
Dzięki. Ogólnie zalecam korzystanie z post_delete sygnał, ponieważ jest bezpieczniejszy w przypadku, gdy usunięcie nie powiedzie się z jakiegokolwiek powodu. Wtedy ani model, ani plik nie zostaną usunięte, zachowując spójność danych. Proszę mnie poprawić, jeśli rozumiem post_delete i pre_delete sygnały są błędne. - toto_tico
Zauważ, że to nie usunie starego pliku, jeśli zastąpisz plik w instancji modelu - Mark
To nie działa dla mnie w Django 1.8 poza adminem. Czy istnieje nowy sposób na zrobienie tego? - caliph
Nie mogę uruchomić go również w Django 1.9 - jonalv


Rozwiązanie Django 1.5: Używam post_delete z różnych powodów, które są wewnętrzne 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)

Utknąłem to na dole pliku models.py.

original_image pole to ImageField w moim Photo Model.


33
2018-05-08 14:15



+1 działa również w 1.6. - nima
Dla każdego, kto używa Amazon S3 jako zaplecza pamięci (poprzez django-magazyny), ta konkretna odpowiedź nie zadziała. Dostaniesz NotImplementedError: This backend doesn't support absolute paths. Możesz to łatwo naprawić, przekazując nazwę pola pliku do storage.delete() zamiast ścieżki pola pliku. Na przykład zamień dwa ostatnie wiersze tej odpowiedzi na storage, name = photo.original_image.storage, photo.original_image.name następnie storage.delete(name). - Sean Azlin
@Sean +1, używam tego dostosowania w 1.7, aby usunąć miniatury wygenerowane przez django-imagekit na S3 przez django-magazyny. docs.djangoproject.com/en/dev/ref/files/storage/... . Uwaga: Jeśli używasz po prostu ImageField (lub FileField), możesz go użyć mymodel.myimagefield.delete(save=False) zamiast. docs.djangoproject.com/en/dev/ref/files/file/... - user2616836
@ user2616836 Czy możesz użyć mymodel.myimagefield.delete(save=False) na post_delete? Innymi słowy, widzę, że mogę usunąć plik, ale czy możesz usunąć ten plik, gdy zostanie usunięty model z obrazem? - eugene
@eugene Tak, możesz, działa (nie jestem pewien dlaczego). W post_delete ty robisz instance.myimagefield.delete(save=False), zanotuj użycie instance. - user2616836


Próbować django-cleanup

pip install django-cleanup

settings.py

INSTALLED_APPS = (
    ...
    'django_cleanup', # should go after your apps
)

33
2018-03-17 17:21



Bardzo wspaniały pakiet. Dziękuję Ci! :) - BoJack Horseman
Po ograniczonych testach mogę potwierdzić, że ten pakiet nadal działa dla Django 1.10. - Deleet
Fajnie, to takie proste - Tunn
Miły. Działa dla mnie na Django 2.0. Używam również S3 jako mojego zaplecza pamięci (django-storages.readthedocs.io/en/latest/backends/...) i szczęśliwie usuwa pliki z S3. - routeburn


Ten kod działa również na Django 1.4 również w panelu 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ć pamięć i ścieżkę przed usunięciem modelu, lub ta ostatnia pozostanie nieważna również po usunięciu.


17
2017-07-04 17:28



To nie działa dla mnie (Django 1.5) i DANGGO 1.3 ZMIANA STANU: "W Django 1.3, gdy model zostanie usunięty, metoda delete () FileField nie zostanie wywołana. Jeśli potrzebujesz oczyszczenia osieroconych plików," Będziesz musiał sam sobie z tym poradzić (na przykład za pomocą niestandardowego polecenia zarządzania, które można uruchamiać ręcznie lub zaplanować okresowe uruchamianie np. za pomocą crona). " - darrinm
Nie działa też dla mnie. Sygnały do. - Antony Hatchkins
To rozwiązanie jest złe! delete nie zawsze jest wywoływane, gdy wiersz jest usunięty, musisz użyć sygnałów. - lvella


ty potrzeba aby usunąć rzeczywisty plik z 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)

7
2017-09-18 01:30





Możesz rozważyć użycie sygnału pre_delete lub post_delete:

https://docs.djangoproject.com/en/dev/topics/signals/

Oczywiście z tych samych powodów usunięto automatyczne usuwanie FileField. Jeśli usuniesz plik, który znajduje się w innym miejscu, będziesz mieć problemy.

W moim przypadku wydawało mi się to odpowiednie, ponieważ miałem dedykowany model plików do zarządzania wszystkimi moimi plikami.

Uwaga: Z jakiegoś powodu post_delete nie działa poprawnie. Plik został usunięty, ale rekord bazy danych pozostał, co jest całkowicie przeciwne do tego, czego oczekiwałbym, nawet w warunkach błędu. pre_delete działa jednak dobrze.


6
2017-11-08 16:50



prawdopodobnie post_delete nie zadziała, ponieważ file_field.delete() domyślnie zapisuje model do db, spróbuj file_field.delete(False)  docs.djangoproject.com/en/1.3/ref/models/fields/... - Adam Jurczyk


Może trochę za późno. Ale najprostszym sposobem dla mnie 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 najlepszą opcją do jej zastąpienia.

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 / signals.py

import os

def delete_image_slide(sender, **kwargs):
    slide = kwargs.get('instance')
    try:
        os.remove(slide.image.path)
    except:
        pass

3
2017-08-16 02:26





Ta funkcja zostanie usunięta w Django 1.3, więc nie będę na niej polegać.

Możesz to zmienić delete metoda danego modelu, aby usunąć plik przed całkowitym usunięciem wpisu z bazy danych.

Edytować:

Oto krótki przykład.

class MyModel(models.Model):

    self.somefile = models.FileField(...)

    def delete(self, *args, **kwargs):
        somefile.delete()

        super(MyModel, self).delete(*args, **kwargs)

1
2018-03-21 02:54



Czy masz przykład, jak tego użyć w modelu, aby usunąć plik? Patrzę na dokumenty i zobacz przykłady, jak usunąć obiekt z bazy danych, ale nie widzę żadnych implementacji usuwania pliku. - narkeeso
Ta metoda jest błędna, ponieważ nie działa przy masowym usuwaniu (jak funkcja "Usuń zaznaczone" administratora). Na przykład MyModel.objects.all()[0].delete() usunie plik podczas MyModel.objects.all().delete() nie będzie. Użyj sygnałów. - Antony Hatchkins


Korzystanie z funkcji post_delete jest z pewnością właściwą drogą. Czasami jednak coś może pójść nie tak, a pliki nie zostaną usunięte. Jest oczywiście przypadek, ż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 nie istnieje odwołanie do obiektu, a następnie usunąć obiekt, jeśli plik nie ma obiektu, a następnie usunąć, również może usunąć na podstawie "aktywny" flagi dla obiekt .. Coś, co dodałem do większości moich modeli. Musisz przekazać obiektom, które chcesz sprawdzić, ścieżkę do plików obiektów, pole pliku i flagę, aby usunąć nieaktywne obiekty:

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

1
2018-06-05 23:22





upewnij się, że piszesz "samego siebie"przed plikiem. Tak powinien być powyższy przykład

def delete(self, *args, **kwargs):
        self.somefile.delete()

        super(MyModel, self).delete(*args, **kwargs)

Zapomniałem o "ja" przed moim plikiem i to nie działało tak, jak wyglądało w globalnej przestrzeni nazw.


0
2018-03-27 17:15





Jeśli masz już kilka nieużywanych plików w swoim projekcie i chcesz je usunąć, możesz użyć narzędzia django django-unused-media


0
2018-01-11 18:50