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

Django自定义用户模型API登录报User not found错误求助

Django自定义用户登录API认证失败排查与解决

问题现象

基于Django开发用户登录API,127.0.0.1:8000/api/register接口可正常创建用户,但调用/api/login接口时返回400错误:

POST /api/login/

HTTP 400 Bad Request

Allow: POST, OPTIONS

Content-Type: application/json

Vary: Accept

错误响应体:

{
    "non_field_errors": [
        "User not found"
    ]
}

已完成的配置操作:

  • 在settings.py中设置auth_user_model = 'account.AppUser'
  • 执行过makemigrations和migrate命令
  • 将原managers.py中的AppUser与CustomUserManager移至models.py文件中

相关代码

account/models.py

from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import gettext_lazy as _

from django.db import models

JOB_TYPE = (
    ('M', "Male"),
    ('F', "Female"),

)

ROLE = (
    ('employer', "Employer"),
    ('employee', "Employee"),
)

'''class User(AbstractUser):
    username = None
    email = models.EmailField(unique=True, blank=False,
                              error_messages={
                                  'unique': "A user with that email already exists.",
                              })
    role = models.CharField(choices=ROLE,  max_length=10)
    gender = models.CharField(choices=JOB_TYPE, max_length=1)


    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.first_name+ ' ' + self.last_name
    objects = CustomUserManager()
 '''

class CustomUserManager(BaseUserManager):
    use_in_migrations = True

    """
    Custom user model manager where email is the unique identifiers
    for authentication instead of usernames.
    """
    def create_user(self, email, password=None):
        """
        Create and save a User with the given email and password.
        """
        if not email:
            raise ValueError('The Email must be set')
        if not password:
            raise ValueError('The Password must be set')
        email = self.normalize_email(email)
        user = self.model(email=email)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password=None):
     if not email:
        raise ValueError('An email is required.')
     if not password:
        raise ValueError('A password is required.')
     user = self.create_user(email, password)
     user.is_superuser = True
     user.save()
     return user     


class AppUser(AbstractBaseUser, PermissionsMixin):
        #user_id = models.AutoField(primary_key=True)
        user_id = models.AutoField(primary_key=True)
        email = models.EmailField(max_length=50, unique=True)
        username = models.CharField(max_length=50)
        is_staff = models.BooleanField(default=False)
        is_active = models.BooleanField(default=True)
        USERNAME_FIELD = 'email'
        REQUIRED_FIELDS = ['username']
        objects = CustomUserManager()
        
        def __str__(self):
            return self.username   

account/serializers.py

from rest_framework import serializers

from django.contrib.auth import get_user_model, authenticate
#from django.core.exceptions import ValidationError

UserModel = get_user_model()

class UserRegisterSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = ['email', 'username', 'password']

    def create_user(self, clean_data):
        user_obj = UserModel.objects.create_user(
            email=clean_data['email'], 
            password=clean_data['password'])
        user_obj.username = clean_data['username']
        user_obj.save()

        return user_obj        

class UserLoginSerializer(serializers.Serializer):
    email = serializers.EmailField()
    password = serializers.CharField()

    ''' def check_user(self, data):
        user = authenticate(
            request=self.context.get('request'),
            email=data['email'],
            password=data['password']
        )   '''

    def validate(self, data):
        email = data.get('email')
        password = data.get('password')

        if email and password:
            user = authenticate(request=self.context.get('request'), email=email, password=password)
   
        if not user:
            raise serializers.ValidationError('User not found')

        if not user.is_active:
            raise serializers.ValidationError('User account is not active.')

        return user 


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserModel
        fields = ('email', 'username')

account/views.py

'''from django.contrib import auth
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect , get_object_or_404
from django.urls import reverse, reverse_lazy

from account.forms import *
from jobapp.permission import user_is_employee 


def get_success_url(request):

    """
    Handle Success Url After LogIN

    """
    if 'next' in request.GET and request.GET['next'] != '':
        return request.GET['next']
    else:
        return reverse('jobapp:home')



def employee_registration(request):

    """
    Handle Employee Registration

    """
    form = EmployeeRegistrationForm(request.POST or None)
    if form.is_valid():
        form = form.save()
        return redirect('account:login')
    context={
        
            'form':form
        }

    return render(request,'account/employee-registration.html',context)



def employer_registration(request):

    """
    Handle Employee Registration 

    """

    form = EmployerRegistrationForm(request.POST or None)
    if form.is_valid():
        form = form.save()
        return redirect('account:login')
    context={
        
            'form':form
        }

    return render(request,'account/employer-registration.html',context)


@login_required(login_url=reverse_lazy('accounts:login'))
@user_is_employee
def employee_edit_profile(request, id=id):

    """
    Handle Employee Profile Update Functionality

    """

    user = get_object_or_404(User, id=id)
    form = EmployeeProfileEditForm(request.POST or None, instance=user)
    if form.is_valid():
        form = form.save()
        messages.success(request, 'Your Profile Was Successfully Updated!')
        return redirect(reverse("account:edit-profile", kwargs={
                                    'id': form.id
                                    }))
    context={
        
            'form':form
        }

    return render(request,'account/employee-edit-profile.html',context)



def user_logIn(request):

    """
    Provides users to logIn

    """

    form = UserLoginForm(request.POST or None)
    

    if request.user.is_authenticated:
        return redirect('/')
    
    else:
        if request.method == 'POST':
            if form.is_valid():
                auth.login(request, form.get_user())
                return HttpResponseRedirect(get_success_url(request))
    context = {
        'form': form,
    }

    return render(request,'account/login.html',context)



def user_logOut(request):
    """
    Provide the ability to logout
    """
    auth.logout(request)
    messages.success(request, 'You are Successfully logged out')
    return redirect('account:login')

    '''

from django.contrib.auth import get_user_model, login, logout
from rest_framework.authentication import SessionAuthentication
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import UserRegisterSerializer, UserLoginSerializer, UserSerializer
from rest_framework import permissions, status
from .validations import custom_validation, validate_email, validate_password


class UserRegister(APIView):
    permission_classes = (permissions.AllowAny,)
    def post(self, request):
        clean_data = custom_validation(request.data)
        serializer = UserRegisterSerializer(data=clean_data)
        if serializer.is_valid(raise_exception=True):
            user = serializer.create(clean_data)
            if user:
                return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(status=status.HTTP_400_BAD_REQUEST)


class UserLogin(APIView):
    permission_classes = (permissions.AllowAny,)
    authentication_classes = (SessionAuthentication,)
    ##
    def post(self, request):
        data = request.data
        assert validate_email(data)
        assert validate_password(data)
        serializer = UserLoginSerializer(data=data)
        if serializer.is_valid(raise_exception=True):
            user = serializer.check_user(data)
            login(request, user)
            return Response(serializer.data, status=status.HTTP_200_OK)


class UserLogout(APIView):
    permission_classes = (permissions.AllowAny,)
    authentication_classes = ()
    def post(self, request):
        logout(request)
        return Response(status=status.HTTP_200_OK)


class UserView(APIView):
    permission_classes = (permissions.IsAuthenticated,)
    authentication_classes = (SessionAuthentication,)
    ##
    def get(self, request):
        serializer = UserSerializer(request.user)
        return Response({'user': serializer.data}, status=status.HTTP_200_OK)

问题根源与修复步骤

1. 登录视图调用未定义方法

UserLogin视图中调用了serializer.check_user(data),但该方法在UserLoginSerializer中被注释,实际生效的是validate方法。

修复:
修改UserLogin视图的post方法,替换调用逻辑并传入请求上下文:

class UserLogin(APIView):
    permission_classes = (permissions.AllowAny,)
    authentication_classes = (SessionAuthentication,)
    
    def post(self, request):
        data = request.data
        assert validate_email(data)
        assert validate_password(data)
        serializer = UserLoginSerializer(data=data, context={'request': request})
        if serializer.is_valid(raise_exception=True):
            user = serializer.validated_data
            login(request, user)
            return Response(UserSerializer(user).data, status=status.HTTP_200_OK)

2. 登录序列化器逻辑漏洞

当前validate方法未处理email或password为空的情况,直接进入错误分支。

修复:
调整UserLoginSerializer的validate方法:

def validate(self, data):
    email = data.get('email')
    password = data.get('password')

    if not email or not password:
        raise serializers.ValidationError('邮箱和密码不能为空')

    user = authenticate(request=self.context.get('request'), email=email, password=password)
   
    if not user:
        raise serializers.ValidationError('User not found')

    if not user.is_active:
        raise serializers.ValidationError('用户账号未激活')

    return user 

3. 注册序列化器字段处理不规范

UserRegisterSerializer自定义的create_user方法手动设置username,存在冗余且可能丢失字段。

修复:
修改注册序列化器,利用ModelSerializer自带逻辑:

class UserRegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)

    class Meta:
        model = UserModel
        fields = ['email', 'username', 'password']

    def create(self, validated_data):
        user = UserModel.objects.create_user(
            email=validated_data['email'],
            username=validated_data['username'],
            password=validated_data['password']
        )
        return user

4. 用户管理器未处理必填字段

CustomUserManager的create_user方法未接收username,但AppUser的REQUIRED_FIELDS包含该字段。

修复:
更新用户管理器方法:

class CustomUserManager(BaseUserManager):
    use_in_migrations = True

    def create_user(self, email, username, password=None):
        if not email:
            raise ValueError('必须设置邮箱')
        if not username:
            raise ValueError('必须设置用户名')
        if not password:
            raise ValueError('必须设置密码')
        email = self.normalize_email(email)
        user = self.model(email=email, username=username)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, username, password=None):
        if not email:
            raise ValueError('必须设置邮箱')
        if not username:
            raise ValueError('必须设置用户名')
        if not password:
            raise ValueError('必须设置密码')
        user = self.create_user(email, username, password)
        user.is_superuser = True
        user.is_staff = True
        user.save()
        return user

5. 验证工具类检查

确保validations.py中的custom_validation、validate_email、validate_password方法能正确校验输入格式,避免因前置验证失败导致认证流程中断。

内容的提问来源于stack exchange,提问作者Ahmed Hussain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:42:03