You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义模型场景下Serializer返回None?原因排查求助

问题排查:自定义User模型登录时password无法从serializer.data获取

问题描述

开发自定义User模型的登录功能时,发现UserLoginSerializer的serializer.data中password始终为None。请求数据request.data包含密码,但serializer.data仅返回email,最终导致authenticate返回None。

相关代码

views.py

class UserLoginView(APIView):
    def post(self, request, format=None):
        serializer = UserLoginSerializer(data=request.data,)
        if serializer.is_valid(raise_exception=True):
            email = serializer.data.get('email')
            password = serializer.data.get('password')
            print(password)
            print(request.data)
            print(serializer.data)
            user = authenticate(email=email, password=password)
            print(user)
            return Response({'msg':'successful login'}, status=status.HTTP_200_OK)

Serializer.py

class UserLoginSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(max_length=255)

    class Meta:
        model = User
        fields = ['email', 'password',]
        extra_kwargs={
            'password':{'write_only': True},
        }

Models.py

class UserManager(BaseUserManager):
    def create_user(self, email, number, name, password=None):
        if not email:
            raise ValueError('Users must have an email address')
        if not number:
            raise ValueError('Users must have a phone number')

        user = self.model(
            email = self.normalize_email(email),
            number = number,
            name = name,
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, number, name, password=None):
        user = self.create_user(
            email,
            number = number,
            password = password,
            name = name,
        )
        user.is_admin = True
        user.save(using=self._db)
        return user

class User(AbstractBaseUser):
    email = models.EmailField(
        verbose_name='email address',
        max_length=255,
        unique=True,
    )
    number=models.CharField(max_length=10)
    name=models.CharField(max_length=30)

    is_admin = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['name','number']

    def __str__(self):
         return self.email

    def has_perm(self, perm, obj=None):
        return self.is_admin

    def has_module_perms(self, app_label):
        return True

    @property
    def is_staff(self):
        return self.is_admin

输出结果

None
{'email': 'g@gmail.com', 'password': '123'}
{'email': 'g@gmail.com'}
None
[06/Feb/2023 18:53:52] "POST /api/user/login/ HTTP/1.1" 200 26

原因分析

  1. write_only: True的作用:在UserLoginSerializer的extra_kwargs中设置'password':{'write_only': True},表示该字段仅用于接收请求输入,不会被序列化输出到serializer.data中,所以从serializer.data取password必然得到None。
  2. serializer.data的特性:serializer.data是序列化后的输出数据,对于write_only字段,Django REST Framework会自动排除,不会包含在其中。
  3. authenticate的要求:authenticate需要传入明文密码进行比对,但serializer.data即使包含密码,也只会是数据库中存储的哈希值,无法直接用于验证。

解决方案

方法1:从request.data直接获取明文密码

class UserLoginView(APIView):
    def post(self, request, format=None):
        serializer = UserLoginSerializer(data=request.data,)
        if serializer.is_valid(raise_exception=True):
            email = serializer.data.get('email')
            # 直接从请求数据中获取明文密码
            password = request.data.get('password')
            user = authenticate(email=email, password=password)
            if user:
                return Response({'msg':'successful login'}, status=status.HTTP_200_OK)
            else:
                return Response({'msg':'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)

方法2:从serializer.validated_data获取验证后的密码(更规范)

class UserLoginView(APIView):
    def post(self, request, format=None):
        serializer = UserLoginSerializer(data=request.data,)
        if serializer.is_valid(raise_exception=True):
            # 从验证后的输入数据中取值
            email = serializer.validated_data.get('email')
            password = serializer.validated_data.get('password')
            user = authenticate(email=email, password=password)
            if user:
                return Response({'msg':'successful login'}, status=status.HTTP_200_OK)
            else:
                return Response({'msg':'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)

关键注意事项

  • 不要移除password字段的write_only: True配置,这是安全最佳实践,避免密码被返回给前端。
  • authenticate方法会自动处理明文密码与数据库哈希密码的比对,无需手动处理哈希逻辑。

内容的提问来源于stack exchange,提问作者Adesh Yadav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 19:02:12