Подтвердить что ты не робот

Модель Django: delete() не срабатывает

У меня есть модель:

class MyModel(models.Model):
 ...
    def save(self):
        print "saving"
        ...
    def delete(self):
        print "deleting"
        ...

Функция save() запускается, но delete() не является. Я использую последнюю версию svn-версии (Django версии 1.2 pre-alpha SVN-11593), и о документации по http://www.djangoproject.com/documentation/models/save_delete_hooks/ это должно работать. Любые идеи?

4b9b3361

Ответ 1

Я думаю, что вы, вероятно, используете функцию массового удаления admin, и сталкиваетесь с тем фактом, что метод группового удаления admin не вызывает delete() (см. связанный ticket).

В прошлом я обошел это, написав собственное действие администратора для удаления моделей.

Если вы не используете метод массового удаления admin (например, вы нажимаете кнопку "Удалить" на странице редактирования объекта), происходит что-то еще.

См. предупреждение здесь:

Действие "удалить выбранные объекты" использует QuerySet.delete() для эффективности причины, которые имеют важное caveat: ваши модели delete() метод не будет вызываться.

Если вы хотите переопределить это поведение, просто напишите пользовательское действие, которое выполняет удаление в вашем предпочтительный способ - например, вызов Model.delete() для каждого из выбранные элементы.

Для получения дополнительной информации об общем дележе, см. документацию по объекту удаление.

Моя пользовательская модель администратора выглядит следующим образом:

from photoblog.models import PhotoBlogEntry
from django.contrib import admin    

class PhotoBlogEntryAdmin(admin.ModelAdmin):
    actions=['really_delete_selected']

    def get_actions(self, request):
        actions = super(PhotoBlogEntryAdmin, self).get_actions(request)
        del actions['delete_selected']
        return actions

    def really_delete_selected(self, request, queryset):
        for obj in queryset:
            obj.delete()

        if queryset.count() == 1:
            message_bit = "1 photoblog entry was"
        else:
            message_bit = "%s photoblog entries were" % queryset.count()
        self.message_user(request, "%s successfully deleted." % message_bit)
    really_delete_selected.short_description = "Delete selected entries"

admin.site.register(PhotoBlogEntry, PhotoBlogEntryAdmin)

Ответ 2

Я знаю, что этот вопрос древний, но я снова столкнулся с этим и хотел добавить, что вы всегда можете переместить свой код в сигнал pre_delete или post_delete, например:

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):
    print("deleting")

Он работает с действием массового удаления администратора (по крайней мере, начиная с 1.3.1).

Ответ 3

Массовое действие admin вызывает queryset.delete().

Вы можете переопределить метод .delete() для набора запросов, поэтому он всегда выполняет удаление объектов 1 на 1. Например:

в менеджерах .py:

from django.db import models
from django.db.models.query import QuerySet

class PhotoQueryMixin(object):
    """ Methods that appear both in the manager and queryset. """
    def delete(self):
        # Use individual queries to the attachment is removed.
        for photo in self.all():
            photo.delete()

class PhotoQuerySet(PhotoQueryMixin, QuerySet):
    pass

class PhotoManager(PhotoQueryMixin, models.Manager):
    def get_query_set(self):
        return PhotoQuerySet(self.model, using=self._db)

В models.py:

from django.db import models

class Photo(models.Model):
    image = models.ImageField(upload_to='images')

    objects = PhotoManager()

    def delete(self, *args, **kwargs):
        # Note this is a simple example. it only handles delete(),
        # and not replacing images in .save()
        super(Photo, self).delete(*args, **kwargs)
        self.image.delete()

Ответ 4

Основная проблема заключается в том, что в Django для массового удаления используется SQL, а не instance.delete(), как указано в другом месте. Для решения, предназначенного только для администратора, следующее решение сохраняет администратор Django "вы действительно хотите удалить эти" межстраничные. Однако решение vdboor является наиболее общим.

from django.contrib.admin.actions import delete_selected

class BulkDeleteMixin(object):
    class SafeDeleteQuerysetWrapper(object):
        def __init__(self, wrapped_queryset):
            self.wrapped_queryset = wrapped_queryset

        def _safe_delete(self):
            for obj in self.wrapped_queryset:
                obj.delete()

        def __getattr__(self, attr):
            if attr == 'delete':
                return self._safe_delete
            else:
                return getattr(self.wrapped_queryset, attr)

        def __iter__(self):
            for obj in self.wrapped_queryset:
                yield obj

        def __getitem__(self, index):
            return self.wrapped_queryset[index]

        def __len__(self):
            return len(self.wrapped_queryset)

    def get_actions(self, request):
        actions = super(BulkDeleteMixin, self).get_actions(request)
        actions['delete_selected'] = (BulkDeleteMixin.action_safe_bulk_delete, 'delete_selected', ugettext_lazy("Delete selected %(verbose_name_plural)s"))
        return actions

    def action_safe_bulk_delete(self, request, queryset):
        wrapped_queryset = BulkDeleteMixin.SafeDeleteQuerysetWrapper(queryset)
        return delete_selected(self, request, wrapped_queryset)


class SomeAdmin(BulkDeleteMixin, ModelAdmin):
    ...

Ответ 5

Используя django v2.2.2, я решил эту проблему с помощью следующего кода

models.py

class MyModel(models.Model):
    file = models.FileField(upload_to=<path>)

    def save(self, *args, **kwargs):
        if self.pk is not None:
            old_file = MyModel.objects.get(pk=self.pk).file
            if old_file.path != self.file.path:
                self.file.storage.delete(old_file.path)

        return super(MyModel, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        ret = super(MyModel, self).delete(*args, **kwargs)
        self.file.storage.delete(self.file.path)
        return ret

admin.py

class MyModelAdmin(admin.ModelAdmin):

    def delete_queryset(self, request, queryset):
        for obj in queryset:
            obj.delete()

Для DefaultAdminSite вызывается delete_queryset, если у пользователя есть правильные разрешения, единственное отличие состоит в том, что исходная функция вызывает queryset.delete(), который не вызывает метод модели delete. Это менее эффективно, так как больше не является массовой операцией, но поддерживает чистоту файловой системы =)