Nadpisanie metody delete modelu django dla masowego usuwania

Nadpisuję metodę delete modelu Django, aby usunąć osierocone pliki z dysku dla pól obrazów, coś takiego:

class Image(models.Model):
    img = models.ImageField(upload_to=get_image_path)
    ...
    def delete(self, *args, **kwargs):
        self.img.delete()
        super(Image, self).delete(*args, **kwargs)

Działa to dobrze, gdy usuwam pojedyncze obiekty z panelu administracyjnego, ale gdy zaznaczam wiele obiektów i usuwam je, nie wydaje się, aby to zostało wywołane. Od jakiegoś czasu googluję, ale nie trafiłem na odpowiednie słowa kluczowe, aby uzyskać odpowiedź na to pytanie, ani oficjalna dokumentacja nie mówi o tym temacie.

Author: Brian Tompsett - 汤莱恩, 2015-03-06

3 answers

: {]}

Metoda delete () wykonuje masowe usuwanie i nie wywołuje żadnych metod delete () w modelach. Emituje jednak sygnały pre_delete i post_delete dla wszystkich usuniętych obiektów (w tym kaskadowych usunięć).

Aby to zadziałało, możesz nadpisać metodę delete na QuerySet, a następnie zastosować QuerySet jako menedżer:

class ImageQuerySet(models.QuerySet):

    def delete(self, *args, **kwargs):
        for obj in self:
            obj.img.delete()
        super(ImageQuerySet, self).delete(*args, **kwargs)

class Image(models.Model):
    objects = ImageQuerySet.as_manager()
    img = models.ImageField(upload_to=get_image_path)
    ...
    def delete(self, *args, **kwargs):
        self.img.delete()
        super(Image, self).delete(*args, **kwargs)
 42
Author: GwynBleidD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 10:01:45

Metoda Delete queryset działa bezpośrednio na bazie danych. Nie wywołuje metod Model.delete(). Z docs :

Należy pamiętać, że w miarę możliwości będzie to wykonywane wyłącznie w języku SQL, a zatem metody delete() poszczególnych instancji obiektów niekoniecznie będą wywoływane podczas procesu. Jeśli podałeś niestandardową metodę delete() w klasie modelu i chcesz mieć pewność, że zostanie ona wywołana, musisz" ręcznie " usunąć instancje tego modelu (np. przez iteracja nad Querysetem i wywołanie delete () na każdym obiekcie z osobna) zamiast używania metody bulk delete () Querysetu.

Jeśli chcesz nadpisać domyślne zachowanie interfejsu administracyjnego Django, możesz napisać niestandardową akcję delete:

Https://docs.djangoproject.com/en/1.7/ref/contrib/admin/actions/

Inną metodą jest nadpisanie post_delete (lub pre_delete) sygnału zamiast delete "metoda": {]}

Https://docs.djangoproject.com/en/1.7/ref/signals/#django.db.models.signals.post_delete

Podobnie jak pre_delete, ale wysyłane na końcu metody delete() modelu i metody delete() queryset.

 7
Author: Selcuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 10:04:55

Uważam, że ten problem jest rozwiązany w docs

Gdzie jest napisane:

Nadpisane metody modelu nie są wywoływane przy operacjach masowych

Zauważ, że metoda delete () dla obiektu niekoniecznie jest wywoływana podczas usuwania obiektów masowo za pomocą zestawu zapytań lub w wyniku kaskadowego usuwania. Aby zapewnić wykonanie niestandardowej logiki usuwania, można użyć sygnałów pre_delete i / lub post_delete.

Niestety, nie ma obejścia, gdy tworzenie lub aktualizowanie obiektów masowo, ponieważ żadne z save (), pre_save i post_save nie są wywoływane.

Jak sugerowano w docs powyżej, uważam, że lepszym rozwiązaniem jest użycie sygnału post_delete, w ten sposób:

from django.db.models.signals import post_delete
from django.dispatch import receiver

class Image(models.Model):
    img = models.ImageField(upload_to=get_image_path)
    ...

@receiver(post_delete, sender=Image)
def delete_image_hook(sender, instance, using, **kwargs):
    instance.img.delete()

W przeciwieństwie do nadpisywania metody delete, Funkcja delete_image_hook powinna być wywoływana również przy usuwaniu zbiorczym i kaskadowym. Oto więcej informacji na temat używania sygnałów Django: https://docs.djangoproject.com/en/1.11/topics/signals/#connecting-to-signals-sent-by-specific-senders

Uwaga na poprzednie odpowiedzi: Niektóre z wcześniejszych postów sugerują nadpisanie metody delete QuerySet, co może mieć wpływ na wydajność i inne niezamierzone zachowanie. Być może te odpowiedzi zostały napisane zanim sygnały Django zostały zaimplementowane, ale myślę, że używanie sygnałów jest czystszym podejściem.

 3
Author: modulitos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-21 21:07:18