builtin globallyavailable admin actions default action which deletes the selected objects this action first displays a confirmation page which shows all the deletable objects or if the user has no permission one of the related childs foreignkeys a permission denied message next it deletes all selected objects and redirects back to the change list populate deletableobjects a data structure of all related objects that will also be deleted the user has already confirmed the deletion do the deletion and return none to display the change list view again return none to display the change list page again display the confirmation page default action which deletes the selected objects this action first displays a confirmation page which shows all the deletable objects or if the user has no permission one of the related childs foreignkeys a permission denied message next it deletes all selected objects and redirects back to the change list populate deletable_objects a data structure of all related objects that will also be deleted the user has already confirmed the deletion do the deletion and return none to display the change list view again return none to display the change list page again display the confirmation page
from django.contrib import messages
from django.contrib.admin import helpers
from django.contrib.admin.decorators import action
from django.contrib.admin.utils import model_ngettext
from django.core.exceptions import PermissionDenied
from django.template.response import TemplateResponse
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy


@action(
    permissions=["delete"],
    description=gettext_lazy("Delete selected %(verbose_name_plural)s"),
)
def delete_selected(modeladmin, request, queryset):
    
    opts = modeladmin.model._meta
    app_label = opts.app_label

    
    
    (
        deletable_objects,
        model_count,
        perms_needed,
        protected,
    ) = modeladmin.get_deleted_objects(queryset, request)

    
    
    if request.POST.get("post") and not protected:
        if perms_needed:
            raise PermissionDenied
        n = len(queryset)
        if n:
            modeladmin.log_deletions(request, queryset)
            modeladmin.delete_queryset(request, queryset)
            modeladmin.message_user(
                request,
                _("Successfully deleted %(count)d %(items)s.")
                % {"count": n, "items": model_ngettext(modeladmin.opts, n)},
                messages.SUCCESS,
            )
        
        return None

    objects_name = model_ngettext(queryset)

    if perms_needed or protected:
        title = _("Cannot delete %(name)s") % {"name": objects_name}
    else:
        title = _("Are you sure?")

    context = {
        **modeladmin.admin_site.each_context(request),
        "title": title,
        "subtitle": None,
        "objects_name": str(objects_name),
        "deletable_objects": [deletable_objects],
        "model_count": dict(model_count).items(),
        "queryset": queryset,
        "perms_lacking": perms_needed,
        "protected": protected,
        "opts": opts,
        "action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
        "media": modeladmin.media,
    }

    request.current_app = modeladmin.admin_site.name

    
    return TemplateResponse(
        request,
        modeladmin.delete_selected_confirmation_template
        or [
            "admin/%s/%s/delete_selected_confirmation.html"
            % (app_label, opts.model_name),
            "admin/%s/delete_selected_confirmation.html" % app_label,
            "admin/delete_selected_confirmation.html",
        ],
        context,
    )