如何在DRF中将已验证标题传递给自定义SlugValidator类?
解决方案
方案一:用序列化器的validate_slug方法替代字段级验证器
字段级验证器本身无法跨字段获取已验证值,直接把SlugsValidator的逻辑迁移到序列化器的validate_slug方法即可——这个方法会在字段自身的验证器执行完成后运行,此时已经能拿到通过TitleValidator验证后的title值:
from rest_framework import serializers from rest_framework.exceptions import ValidationError class TitleValidator: MIN_TITLE_LENGTH = 20 def __call__(self, title: str): if len(title) < self.MIN_TITLE_LENGTH: raise ValidationError(f"Min title length is {self.MIN_TITLE_LENGTH}") return title class PostSerializer(serializers.ModelSerializer): title = serializers.CharField(validators=[TitleValidator()]) slug = serializers.CharField(max_length=100, required=False) def validate_slug(self, slug): # 直接获取已验证的title值 validated_title = self.validated_data.get('title') # 执行原SlugsValidator的业务逻辑 if not slug: # 示例:用title自动生成slug slug = validated_title.lower().replace(' ', '-') # 可添加其他slug格式验证 if not slug.islower() or ' ' in slug: raise ValidationError("Slug必须为小写且不含空格") return slug
方案二:给自定义验证器传递序列化器上下文
如果坚持要用类形式的验证器,可以在序列化器初始化时,把自身实例传递给SlugsValidator的上下文,让验证器能访问到已验证的字段数据:
from rest_framework import serializers from rest_framework.exceptions import ValidationError class TitleValidator: MIN_TITLE_LENGTH = 20 def __call__(self, title: str): if len(title) < self.MIN_TITLE_LENGTH: raise ValidationError(f"Min title length is {self.MIN_TITLE_LENGTH}") return title class SlugsValidator: def __init__(self, dependent_field): self.dependent_field = dependent_field self.context = {} def __call__(self, slug): # 从上下文获取序列化器实例,进而拿到已验证的title serializer = self.context.get('serializer') validated_title = serializer.validated_data.get(self.dependent_field) # 执行你的验证逻辑 if not slug: slug = validated_title.lower().replace(' ', '-') # 其他验证规则 return slug class PostSerializer(serializers.ModelSerializer): title = serializers.CharField(validators=[TitleValidator()]) slug = serializers.CharField( max_length=100, required=False, validators=[SlugsValidator(dependent_field='title')] ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 将序列化器实例传递给SlugsValidator的上下文 for validator in self.fields['slug'].validators: if isinstance(validator, SlugsValidator): validator.context = {'serializer': self}
注:DRF会按字段声明顺序执行验证,title先于slug完成验证,所以validated_data中会存在已通过校验的title值。
补充说明
字段级验证器的设计初衷是独立验证单个字段,跨字段的验证逻辑更适合放在序列化器的validate_<field>或全局validate方法中。上述两种方案都无需使用Meta类的全局验证器,其中方案一的写法更简洁,符合DRF的常规开发习惯。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

