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

У меня есть 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 должен быть первым параметром.

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

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

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

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

4 ответа

Решение

Вы на правильном пути со своим 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]

Вы можете использовать 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 объектов.

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

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

Это конечный выход,

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

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


Это объяснение по поводу delete_model.

Метод delete_model получает HttpRequest и экземпляр модели. Переопределение этого метода позволяет выполнять операции до или после удаления. Вызовите 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-й объект, чтобы удалить его из "окна изменений".

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

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

Это конечный выход,

Таким образом, вы получите выбранный объект как один из 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)

Основная проблема заключается в том, что при массовом удалении администратора 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):
    ...

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

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

Администратор delete_model() такой же, как модель delete() https://github.com/django/django/blob/master/django/contrib/admin/options.py

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам