You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django Admin修改页删除所有内联对象时保留第一个

解决方案

要实现删除所有内联Email对象时保留第一个,你可以通过自定义内联表单集来控制删除逻辑,具体步骤如下:

1. 自定义表单集,添加验证逻辑

在admin.py中定义继承自BaseInlineFormSet的表单集,重写clean方法来确保至少保留一个Email对象:

# admin.py
from django.contrib import admin
from django.forms import BaseInlineFormSet
from django.core.exceptions import ValidationError
from .models import Person, Email

class EmailInlineFormSet(BaseInlineFormSet):
    def clean(self):
        # 先执行父类的clean逻辑
        super().clean()
        
        # 筛选出未被标记删除且验证通过的表单
        kept_forms = []
        for form in self.forms:
            # 跳过未修改的空表单(新增的未填写的表单)
            if form.cleaned_data.get('DELETE'):
                continue
            if form.is_valid() and not form.cleaned_data.get('DELETE'):
                kept_forms.append(form)
        
        # 如果没有保留的有效表单,抛出验证错误
        if len(kept_forms) == 0:
            raise ValidationError("必须至少保留一个邮箱地址。")

2. 关联表单集到内联类

修改EmailInline,将自定义的表单集指定给它:

class EmailInline(admin.TabularInline):
    model = Email
    # 绑定自定义表单集
    formset = EmailInlineFormSet

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    inlines = (EmailInline,)

效果说明

当你尝试删除所有内联的Email对象时,Admin页面会弹出验证错误提示,阻止提交操作,强制保留至少一个有效的邮箱记录。如果你希望自动取消第一个对象的删除标记(不提示错误),可以修改表单集的clean方法:

class EmailInlineFormSet(BaseInlineFormSet):
    def clean(self):
        super().clean()
        # 统计被标记删除的表单数量
        deleted_count = sum(1 for form in self.forms if form.cleaned_data.get('DELETE'))
        
        # 如果所有表单都被标记删除
        if deleted_count == len(self.forms):
            # 取消第一个表单的删除标记
            self.forms[0].cleaned_data['DELETE'] = False
            # 清除该表单的删除相关错误
            if 'DELETE' in self.forms[0].errors:
                del self.forms[0].errors['DELETE']

这种方式会自动保留第一个Email对象,无需用户手动取消删除操作。


内容的提问来源于stack exchange,提问作者Super Kai - Kazuya Ito

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 13:52:25