Django admin: переопределить метод удаления

У меня есть admin.py следующим образом:

  class profilesAdmin(admin.ModelAdmin):
     list_display = ["type","username","domain_name"]

Теперь я хочу выполнить некоторые действия перед удалением объекта:

  class profilesAdmin(admin.ModelAdmin):
     list_display = ["type","username","domain_name"]

     @receiver(pre_delete, sender=profile)
     def _profile_delete(sender, instance, **kwargs):
        filename=object.profile_name+".xml"
        os.remove(os.path.join(object.type,filename))

Если я использую метод сигнала удаления, как это, я получаю сообщение об ошибке, указывающее, что self должен быть первым параметром.

Как изменить приведенную выше функцию? И я хочу получить имя_файла удаляемого объекта. Как это можно сделать?

Я также попытался переопределить метод delete_model:

def delete_model(self, request, object):
    filename=object.profile_name+".xml"
    os.remove(os.path.join(object.type,filename))
    object.delete()

Но это не работает, если несколько объектов должны быть удалены одним выстрелом.

Ответ 1

Вы на правильном пути с вашим методом delete_model. Когда администратор django выполняет действие над несколькими объектами одновременно, он использует функцию обновления. Однако, как видно из документов, эти действия выполняются на уровне базы данных только с использованием SQL.

Вам нужно добавить свой метод delete_model в качестве пользовательского действия в администраторе django.

def delete_model(modeladmin, request, queryset):
    for obj in queryset:
        filename=obj.profile_name+".xml"
        os.remove(os.path.join(obj.type,filename))
        obj.delete()

Затем добавьте свою функцию в ваш modeladmin -

class profilesAdmin(admin.ModelAdmin):
    list_display = ["type","username","domain_name"]
    actions = [delete_model]

Ответ 2

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

Самое общее решение - переопределить запрос, возвращенный диспетчером модели, для перехвата удаления.

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, admin.ModelAdmin):
    ...

Ответ 3

вы пытаетесь переопределить метод delete_model, потому что, когда вы удаляете несколько объектов, django использует QuerySet.delete(), по соображениям эффективности ваш метод delete() не будет вызываться.

вы можете увидеть его там https://docs.djangoproject.com/en/1.9/ref/contrib/admin/actions/
следить за началом Предупреждение

Admin delete_model() совпадает с моделью delete() https://github.com/django/django/blob/master/django/contrib/admin/options.py#L1005

поэтому при удалении нескольких объектов пользовательский метод удаления никогда не будет вызываться.

у вас есть два пути.

1.Удалить действие удаления.
действие вызывает Model.delete() для каждого из выбранных элементов.

сигнал 2.use.
вы можете использовать сигнал самостоятельно, а не внутри класса.

вы также можете посмотреть этот вопрос модель Django: delete() не запускается

Ответ 4

Ваш метод должен быть

class profilesAdmin(admin.ModelAdmin):
    #...

    def _profile_delete(self, sender, instance, **kwargs):
        # do something

    def delete_model(self, request, object):
        # do something

Вы должны добавить ссылку на текущий объект в качестве первого аргумента в каждой сигнатуре метода (обычно называемой self). Кроме того, delete_model должен быть реализован как метод.

Ответ 5

Вы можете использовать delete_queryset из Django 2.1 и выше для массового удаления объектов и delete_model для одиночного удаления. Оба метода будут обрабатывать что-то перед удалением объекта.

ModelAdmin.delete_queryset (запрос, набор запросов)


Это объяснение delete_queryset в примечании к выпуску Django 2.1.

Метод delete_queryset() получает HttpRequest и QuerySet объектов, которые необходимо удалить. Переопределите этот метод, чтобы настроить процесс удаления для "удаления выбранных объектов"

Давайте посмотрим, что делает delete_queryset, вы можете переопределить admin. Класс ModelAdmin таким образом, включая функцию delete_queryset. Здесь вы получите список объектов, а queryset.delete() означает удаление всех объектов одновременно, или вы можете добавить цикл для удаления один за другим.

def delete_queryset(self, request, queryset):
    print('==========================delete_queryset==========================')
    print(queryset)

    """
    you can do anything here BEFORE deleting the object(s)
    """

    queryset.delete()

    """
    you can do anything here AFTER deleting the object(s)
    """

    print('==========================delete_queryset==========================')

Итак, я собираюсь удалить 5 объектов из "окна выбора", и вот эти 5 объектов.

deleting 5 objects from "select window"

Затем вы будете перенаправлены на страницу подтверждения, как это,

going to delete those 5 objects

Помните о кнопке "Да, я уверен", и я объясню это позже. Когда вы нажмете эту кнопку, вы увидите изображение ниже после удаления этих 5 объектов.

successfully deleted 5 objects

Это вывод терминала,

terminal output of those 5 objects

Таким образом, вы получите эти 5 объектов в виде списка QuerySet, и перед удалением вы можете делать все что угодно в области комментариев.

ModelAdmin.delete_model (запрос, объект)


Это объяснение о delete_model.

Метод delete_model получает HttpRequest и экземпляр модели. Переопределение этого метода позволяет выполнять pre- или операции после удаления. Вызовите super(). Delete_model(), чтобы удалить объект с помощью Model.delete().

Давайте посмотрим, что делает delete_model, вы можете переопределить admin. Класс ModelAdmin таким образом, включая функцию delete_model.

actions = ['delete_model']

def delete_model(self, request, obj):
    print('============================delete_model============================')
    print(obj)

    """
    you can do anything here BEFORE deleting the object
    """

    obj.delete()

    """
    you can do anything here AFTER deleting the object
    """

    print('============================delete_model============================')

Я просто щелкаю свой 6-й объект, чтобы удалить его из "окна изменения".

deleting 1 object from "change window"

Есть еще одна кнопка "Удалить", при нажатии на которую вы увидите окно, которое мы видели ранее.

going to delete those 1 object

Нажмите кнопку "Да, я уверен", чтобы удалить один объект. Вы увидите следующее окно с уведомлением об этом удаленном объекте.

successfully deleted 1 object

Это вывод терминала,

terminal output of those 1 object

Таким образом, вы получите выделенный объект как один из QuerySet, и перед удалением вы можете делать все что угодно в области комментариев.


Окончательный вывод: вы можете обработать событие удаления, нажав кнопку "Да, я уверен" в "окне выбора" или "окне изменения" на сайте администратора Django, используя delete_queryset и delete_model. Таким образом, нам не нужно обрабатывать такие сигналы, как django.db.models.signals.pre_delete или django.db.models.signals.post_delete.

Вот полный код,

from django.contrib import admin

from . import models

class AdminInfo(admin.ModelAdmin):
    model = models.AdminInfo
    actions = ['delete_model']

    def delete_queryset(self, request, queryset):
        print('========================delete_queryset========================')
        print(queryset)

        """
        you can do anything here BEFORE deleting the object(s)
        """

        queryset.delete()

        """
        you can do anything here AFTER deleting the object(s)
        """

        print('========================delete_queryset========================')

    def delete_model(self, request, obj):
        print('==========================delete_model==========================')
        print(obj)

        """
        you can do anything here BEFORE deleting the object
        """

        obj.delete()

        """
        you can do anything here AFTER deleting the object
        """

        print('==========================delete_model==========================')

admin.site.register(models.AdminInfo, AdminInfo)