DRF邀请码验证问题:模型Unique字段导致自定义验证逻辑失效
解决DRF邀请码验证反向逻辑问题
我用DRF开发用户注册的自定义邀请系统,需要单独接口验证邀请码是否有效。但因为InviteCode模型的code字段设置了unique=True,导致数据库中存在该邀请码时,Serializer的is_valid()返回False,而我需要的逻辑是邀请码存在时验证通过,不存在时报错。
现有代码
模型代码
class InviteCode(models.Model): code = models.CharField(max_length=32, null=False, unique=True) referal = models.ForeignKey(to=User, null=True, blank=True, on_delete=models.CASCADE, related_name='%(class)s_referal') is_teacher = models.BooleanField(default=False) used_by = models.ForeignKey(to=User, null=True, blank=True, on_delete=models.CASCADE, related_name='%(class)s_used_by')
原Serializer及尝试的自定义验证
class InviteCodeSerializer(serializers.ModelSerializer): class Meta: model = InviteCode fields = "__all__" # 尝试添加的自定义验证,但未生效 def validate_code(self, value): code = InviteCode.objects.filter(code = value).first() if code is None: raise serializers.ValidationError("Invite code doesn't exist") # Other validation checks return code
错误信息
调用is_valid()后返回的错误:
{'code': [ErrorDetail(string='invite code with this code already exists.', code='unique')]}
问题原因
ModelSerializer会自动继承模型字段的unique约束校验,这个校验的执行优先级高于自定义的validate_code方法,所以当邀请码已存在时,先触发了重复错误,自定义逻辑根本没机会执行。
解决方案
方法一:移除ModelSerializer的默认Unique校验
在Serializer的Meta配置中,通过extra_kwargs清空code字段的默认校验器,让自定义验证逻辑接管:
from rest_framework import serializers from .models import InviteCode class InviteCodeValidateSerializer(serializers.ModelSerializer): class Meta: model = InviteCode fields = ['code'] # 只需要验证code字段,不需要全字段 extra_kwargs = { 'code': {'validators': []} # 移除默认的unique校验器 } def validate_code(self, value): code_obj = InviteCode.objects.filter(code=value).first() if not code_obj: raise serializers.ValidationError("邀请码不存在") # 可添加额外校验,比如判断邀请码是否已被使用 if code_obj.used_by is not None: raise serializers.ValidationError("该邀请码已被使用") return value
方法二:使用基础Serializer替代ModelSerializer
如果只是做邀请码验证,不需要序列化整个模型对象,直接用基础Serializer更灵活,完全避免继承模型的默认校验:
from rest_framework import serializers from .models import InviteCode class InviteCodeValidateSerializer(serializers.Serializer): code = serializers.CharField(max_length=32) def validate_code(self, value): code_obj = InviteCode.objects.filter(code=value).first() if not code_obj: raise serializers.ValidationError("邀请码不存在") # 额外校验逻辑示例 if code_obj.used_by is not None: raise serializers.ValidationError("该邀请码已被使用") # 把验证后的邀请码对象存入context,方便视图后续调用 self.context['invite_code'] = code_obj return value
视图使用示例
from rest_framework.views import APIView from rest_framework.response import Response from .serializers import InviteCodeValidateSerializer class InviteCodeCheckAPIView(APIView): def post(self, request): serializer = InviteCodeValidateSerializer(data=request.data) if serializer.is_valid(): # 可从context获取验证后的邀请码对象 invite_code = serializer.context.get('invite_code') return Response({"message": "邀请码有效"}, status=200) return Response(serializer.errors, status=400)
内容的提问来源于stack exchange,提问作者Makarenko Nick
相关产品推荐
相关产品推荐

