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

Django Admin如何实现PurchaseItem模型两个外键二选一必填校验

Django Admin 实现PurchaseItem模型校验的方案

方案1:模型层全局校验(推荐)

该方案的校验规则会作用于所有数据保存场景,包括Admin后台、API接口、shell操作等,避免校验规则遗漏。
修改你的PurchaseItem模型代码如下:

from django.db import models
from django.core.exceptions import ValidationError

class PurchaseItem(models.Model):
    product = models.ForeignKey("products.Product", on_delete=models.CASCADE, blank=True, null=True)
    product_attribute = models.ForeignKey("products.ProductAttribute", on_delete=models.CASCADE, blank=True, null=True)

    def clean(self):
        # 校验两个字段不能同时不为空
        if self.product_id and self.product_attribute_id:
            raise ValidationError("产品和产品属性不能同时选择,请只保留其中一项")
        # 校验两个字段不能同时为空
        if not self.product_id and not self.product_attribute_id:
            raise ValidationError("请选择产品或者产品属性中的一项")
        # 执行父类默认校验逻辑
        super().clean()

添加上述代码后无需额外修改Admin配置,Admin后台保存数据时会自动触发该校验,校验不通过时会直接在表单页展示错误提示。

注意:如果你在Admin之外的场景手动调用模型save()方法,需要先执行obj.full_clean()才会触发该校验逻辑。

方案2:仅Admin层校验

如果你的校验规则只需要在Admin后台生效,可以直接在对应的ModelAdmin中重写校验方法:

from django.contrib import admin
from django.core.exceptions import ValidationError
from .models import PurchaseItem

class PurchaseItemAdmin(admin.ModelAdmin):
    def clean_form(self, request, form, change):
        cleaned_data = form.cleaned_data
        product = cleaned_data.get("product")
        product_attribute = cleaned_data.get("product_attribute")

        if product and product_attribute:
            raise ValidationError("产品和产品属性不能同时选择,请只保留其中一项")
        if not product and not product_attribute:
            raise ValidationError("请选择产品或者产品属性中的一项")
        
        return super().clean_form(request, form, change)

admin.site.register(PurchaseItem, PurchaseItemAdmin)

该方案的校验逻辑仅在Admin后台提交表单时触发,其他数据保存场景不会生效。

内容的提问来源于stack exchange,提问作者aimenanep

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 14:45:07