Django 4.2 ModelAdmin单对象删除失败时如何屏蔽成功提示?
问题描述
在Django 4.2的ModelAdmin中自定义了删除逻辑,批量删除时失败提示显示正常,但单个删除(经confirm_delete.html确认后)明明删除失败(符合预期),页面却同时弹出成功和失败两种提示。
原因分析
delete_model方法缩进错误,未被重写
你提供的代码中,delete_model是全局函数而非ModelAdmin类的成员方法,Django会忽略这个自定义逻辑,转而执行默认的delete_model——默认逻辑会直接删除数据库中的对象,导致response_delete检测到对象不存在时弹出成功提示。自定义API删除逻辑未关联数据库删除动作
即使修复缩进,当前delete_model在API删除成功时未调用obj.delete(),数据库对象不会被删除,response_delete会误判为删除失败;而API删除失败时,默认逻辑(如果未被正确覆盖)仍会删除数据库对象,导致成功提示和自定义的失败提示同时出现。response_delete判断逻辑脱离实际删除结果
当前response_delete仅通过数据库中是否存在对象判断删除状态,未关联API的实际返回结果,导致状态判断不准确。
修复方案
步骤1:修复delete_model的缩进
将delete_model缩进至ModelAdmin类内部,确保Django识别并使用自定义逻辑。
步骤2:完善delete_model逻辑
在API删除成功后手动删除数据库对象,失败时保留对象并添加错误提示,所有状态提示直接在delete_model中处理,避免response_delete重复判断。
步骤3:简化response_delete
仅保留跳转逻辑,删除原本的状态判断和提示添加,因为结果已在delete_model中处理完毕。
完整修复代码
class MailboxAdmin(admin.ModelAdmin): # 其他配置... def get_actions(self, request): actions = super().get_actions(request) del actions['delete_selected'] return actions def really_delete_selected(self, request, queryset): ''' # 1. Update the group-mailbox relation: `goto` values if mailbox(es) are deleted # 2. Sending a mail to the current user with CSV of mailboxes deleted # 3. Used domain space will reduce by the maxquota of mailbox ''' response = None try: mbox_list = [] failed_deletions = [] for obj in queryset: # COMPLETE MAILBOX DELETION OF FOLDERS, DOVECOT ENTRY, ADMIN PANEL PERMANENTLY api_delete_status = call_purge_mailbox_api(request, obj) response = api_delete_status if response['status_code'] == 200: # Set the quota value after deletion of mailbox(es) mdomain = Domain.objects.get(id=obj.domain.pk) mdomain.quota -= obj.maxquota mdomain.save() mbox_list.append(obj.email) # Remove the user from the group mailing lists removed_from_groups = remove_from_group_lists(obj.email, mbox_list) # 删除数据库中的对象 obj.delete() else: failed_deletions.append(obj.email) if mbox_list: self.message_user(request, f"Successfully deleted {len(mbox_list)} mailbox(es).", level='success') for email in failed_deletions: self.message_user(request, f"Failed to delete {email}. Mailbox absent. Contact helpdesk.", level='error') if not mbox_list and not failed_deletions: self.message_user(request, "No mailboxes were selected for deletion.", level='error') except Exception as e: self.message_user(request, f"Failed to delete items: {str(e)}", level='error') really_delete_selected.short_description = "Delete selected entries" def delete_model(self, request, obj): try: # Perform the deletion via API api_delete_status = call_purge_mailbox_api(request, obj) if isinstance(api_delete_status, dict) and 'status_code' in api_delete_status: status_code = api_delete_status['status_code'] if status_code == 200: # API删除成功,再删除数据库对象 mdomain = Domain.objects.get(id=obj.domain.pk) mdomain.quota -= obj.maxquota mdomain.save() # Remove the user from the group mailing lists removed_from_groups = remove_from_group_lists(obj.email, [obj]) obj.delete() self.message_user(request, f"Successfully deleted {obj.email}.", level='success') else: # API删除失败,保留数据库对象并提示错误 self.message_user(request, f"Failed to delete mailbox {obj.email}. Contact Helpdesk", level='error') else: self.message_user(request, f"Invalid response from deletion API for {obj.email}.", level='error') except Exception as e: self.message_user(request, f"Failed to delete {obj.email}. Error: {str(e)}", level='error') def response_delete(self, request, obj_display, obj_id): # 仅跳转回列表页,提示已在delete_model中处理 return HttpResponseRedirect(reverse('admin:myapp_mailbox_changelist'))
关键说明
- 修复缩进后,
delete_model会被Django正确调用,替代默认逻辑。 - API删除成功后才调用
obj.delete(),确保数据库对象和实际邮箱资源同步删除。 - 所有提示信息都在
delete_model和really_delete_selected中处理,response_delete仅负责跳转,避免重复判断导致的错误提示。 - 批量删除的
really_delete_selected中也添加了obj.delete(),确保数据库对象被同步删除。
内容的提问来源于stack exchange,提问作者user956424

