Администратор 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 должен быть реализован как метод.