如何在Django models中实现对象级别的自定义验证
Django模型层实现对象级自定义验证方案
完全可以在Django的模型层实现你需要的对象级条件校验,核心是重写模型的clean()方法,配合错误合并逻辑即可实现所有验证错误统一返回,具体实现步骤如下:
第一步:修改User模型,添加自定义验证逻辑
首先你需要在模型中重写clean()方法,把条件必填的校验逻辑放在这里,注意你之前示例里的HELPSEEKER是笔误,你的模型中定义的供方常量是SUPPLY,修正后的代码如下:
from django.db import models from django.contrib.auth.models import AbstractUser from django.core.exceptions import ValidationError class User(AbstractUser): SUPPLY = 'Supply' DEMAND = "Demand" USER_TYPES = [ (SUPPLY, 'Supply'), (DEMAND, 'Demand') ] user_type = models.CharField( max_length=20, choices=USER_TYPES, ) username = models.CharField(max_length=50, unique=True) email = models.CharField(max_length=50, unique=True) profile_picture_url = models.CharField(max_length=250) # 保留blank=True,因为是条件必填不是全局必填 country = models.CharField(max_length=50, blank=True) def clean(self): # 先执行父类自带的字段验证(比如字段长度、唯一键约束前置检查等) super().clean() errors = {} # 供方用户country必填校验 if self.user_type == self.SUPPLY and (not self.country or self.country.strip() == ''): errors['country'] = ['该字段为供方用户必填项'] # 其他模型层自定义校验都可以在这里添加,错误统一放到errors字典 # if 其他校验条件: # errors['其他字段名'] = ['错误提示'] # 有错误则统一抛出 if errors: raise ValidationError(errors) # 可选:如果需要所有调用save()的场景都自动触发校验,可重写save方法 def save(self, *args, **kwargs): self.full_clean() super().save(*args, **kwargs)
第二步:修改序列化器逻辑,合并所有验证错误
你之前序列化器的问题是遇到第一个错误就直接抛出,导致错误不能一次性返回,同时需要把模型层的验证错误也合并到返回结果中,修改后的序列化器代码如下:
from rest_framework import serializers from django.contrib.auth.hashers import make_password from django.contrib.auth import validations from django.core.exceptions import ValidationError from .models import User class UserSerializer(serializers.ModelSerializer): password = serializers.CharField(write_only=True) password_confirmation = serializers.CharField(write_only=True) def validate(self, data): errors = {} password = data.get('password') password_confirmation = data.get('password_confirmation') # 校验密码一致性 if password and password_confirmation and password != password_confirmation: errors['password_confirmation'] = '两次输入的密码不匹配' # 校验密码强度 if password: try: validations.validate_password(password=password) except ValidationError as err: errors['password'] = err.messages # 触发模型层校验,合并模型层错误 temp_user_data = {k:v for k, v in data.items() if k not in ['password', 'password_confirmation']} temp_user = User(**temp_user_data) try: temp_user.full_clean() except ValidationError as e: errors.update(e.message_dict) # 所有校验完成后,有错误则统一抛出 if errors: raise ValidationError(errors) # 校验通过后处理密码 data.pop('password_confirmation') data['password'] = make_password(password) return data class Meta: model = User fields = '__all__'
实现效果
现在无论是模型层的字段校验错误、自定义条件校验错误,还是序列化器层的密码校验错误,都会被合并到同一个错误字典中一次性返回,不会出现先后抛出的问题,满足你的需求。
内容的提问来源于stack exchange,提问作者userX
相关产品推荐
相关产品推荐

