重写rest-auth的RegisterSerializer,添加用户年龄校验
嘿,我帮你捋捋这个问题——你之前用@property做年龄校验没生效,反而导致用户被创建还打不开详情页,核心原因是校验时机不对:模型层的setter或者@property校验要么没被DRF的序列化器触发,要么在用户已经入库后才抛出错误,自然拦不住用户创建,还会导致后续访问出问题。下面给你两种靠谱的实现方式,首推第一种:
方法1:在注册序列化器中加校验(最符合DRF流程)
rest-auth的注册逻辑是靠序列化器处理请求数据的,所以在序列化器层面做校验,能在数据入库前就拦截不符合条件的请求,这是最合理的做法。
步骤很简单:
- 自定义一个注册序列化器,继承rest-auth自带的
RegisterSerializer - 添加上年龄字段,并重写校验方法
- 配置settings让rest-auth用你的自定义序列化器
示例代码:
from rest_auth.registration.serializers import RegisterSerializer from rest_framework import serializers class CustomRegisterSerializer(RegisterSerializer): # 先把年龄字段加到序列化器里(如果还没加的话) age = serializers.IntegerField(required=True, help_text="年龄必须≥18") # 专门校验年龄的方法,DRF会自动调用 def validate_age(self, value): if value < 18: raise serializers.ValidationError("必须年满18岁才能注册账号") return value # 重写save方法,把校验通过的年龄存到用户模型里 def save(self, request): user = super().save(request) user.age = self.validated_data.get('age') user.save() return user
然后在项目的settings.py里配置这个序列化器:
REST_AUTH_REGISTER_SERIALIZERS = { 'REGISTER_SERIALIZER': '你的app名称.serializers.CustomRegisterSerializer', }
这样一来,只要前端传的年龄小于18,序列化器会直接返回错误,根本不会创建用户,完美解决你的问题。
方法2:在用户模型的clean方法中做校验
如果你想在模型层做兜底校验,别用@property,而是重写模型的clean方法,并且在save时手动触发它(默认DRF创建模型不会自动调用clean)。
示例代码:
from django.contrib.auth.models import AbstractUser from django.core.exceptions import ValidationError from django.db import models class CustomUser(AbstractUser): age = models.IntegerField(null=False, blank=False) def clean(self): super().clean() # 校验年龄 if self.age < 18: raise ValidationError({"age": "必须年满18岁才能注册"}) def save(self, *args, **kwargs): # 保存前先触发clean做校验 self.full_clean() super().save(*args, **kwargs)
不过这种方式要注意:如果用默认的rest-auth序列化器,模型抛出的ValidationError需要序列化器能正确捕获并返回给前端,不然可能会出现500错误。所以还是第一种序列化器的方式更稳妥。
为啥之前的@property方式不行?
你之前用@property的setter做校验,比如:
@property def age(self): return self._age @age.setter def age(self, value): if value <18: raise ValidationError(...) self._age = value
问题在于DRF序列化器给模型字段赋值时,通常是直接操作数据库字段(比如user.age = value),不会走你定义的setter方法,导致校验根本没触发;就算触发了,也是在用户已经被保存到数据库之后,这时候抛错已经晚了,还会导致后续访问用户详情页时再次触发错误,所以打不开页面。
总结一下,优先用序列化器层面的校验,既符合DRF的设计逻辑,又能精准拦截无效请求~
内容的提问来源于stack exchange,提问作者slqq

