Django REST Framework如何定义必填字段同时触发自定义validate方法
解决方案
问题原因
DRF 默认校验逻辑执行顺序为:先运行单字段级别校验(包含必填校验、类型校验、自定义validate_xxx方法),如果该阶段出现错误,会直接终止校验流程返回错误,不会执行后续全局validate方法,这就是你设置字段必填后交叉校验逻辑不触发的原因。
可行实现方案
重写序列化器的run_validation方法,先收集字段校验阶段的错误,再强制执行全局交叉校验逻辑,合并所有错误后统一抛出即可。既可以复用DRF原生的字段必填校验能力,也能保证你的联系信息校验逻辑始终执行。
修改后的序列化器代码示例:
class ValidateNewCoopSerializer(serializers.Serializer): # 必填字段直接声明required=True,使用原生校验能力 coop_name=serializers.CharField(required=True) websites=serializers.CharField(required=True) contact_name=serializers.CharField(required=True) contact_name_public=serializers.CharField(required=True) entity_types=serializers.CharField(required=True) req_reason=serializers.CharField(required=True) # 非必填字段保持原有定义即可 id=serializers.CharField(required=False, allow_blank=True) street=serializers.CharField(required=False, allow_blank=True) address_public=serializers.CharField(required=False, allow_blank=True) city=serializers.CharField(required=False, allow_blank=True) state=serializers.CharField(required=False, allow_blank=True) zip=serializers.CharField(required=False, allow_blank=True) county=serializers.CharField(required=False, allow_blank=True) country=serializers.CharField(required=False, allow_blank=True) contact_email=serializers.CharField(required=False, allow_blank=True) contact_email_public=serializers.CharField(required=False, allow_blank=True) contact_phone=serializers.CharField(required=False, allow_blank=True) contact_phone_public=serializers.CharField(required=False, allow_blank=True) scope=serializers.CharField(required=False, allow_blank=True) tags=serializers.CharField(required=False, allow_blank=True) desc_english=serializers.CharField(required=False, allow_blank=True) desc_other=serializers.CharField(required=False, allow_blank=True) def run_validation(self, data=serializers.empty): field_errors = {} validated_data = {} # 先执行原生字段校验,捕获字段级错误 try: validated_data = super().run_validation(data) except serializers.ValidationError as e: field_errors = e.detail # 提取已校验通过的字段值,用于后续全局校验 if len(e.args) > 0 and isinstance(e.args[0], dict): validated_data = e.args[0] # 强制执行全局交叉校验逻辑 global_errors = {} contact_email = validated_data.get('contact_email', '').strip() contact_phone = validated_data.get('contact_phone', '').strip() if not contact_email and not contact_phone: global_errors['contact'] = 'Either contact phone or contact email is required.' # 合并所有错误统一抛出 all_errors = {**field_errors, **global_errors} if all_errors: raise serializers.ValidationError(all_errors) return validated_data
视图层代码不需要做任何修改,原有逻辑可以直接复用。该方案完全兼容你使用的Django REST framework 3.12.2版本。
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

