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

Django中AbstractUser模型下authenticate返回None的解决方法

Django AbstractUser 登录 authenticate 返回 None 问题解决

问题背景

基于Django搭建求职网站,使用AbstractUser自定义求职者模型JobseekerRegisterInfo,注册功能正常,但登录时authenticate方法始终返回None,控制台输出USER IS NONE。

现有代码片段

自定义用户模型

class JobseekerRegisterInfo(AbstractUser):
    username = first_name = last_name = None
    id = models.BigAutoField(primary_key=True)
    phone_number = PhoneNumberField()
    email = models.EmailField(unique=True)
    full_name = models.CharField(max_length=120)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['phone_number', 'full_name', 'hashed_password']

    def __str__(self):
        return self.full_name

登录表单

class JobseekerLoginForm(forms.Form):
    email = forms.EmailField(label='Введіть ваш Email: ',
                             widget=forms.EmailInput(attrs={'class': 'form-control'}))
    password = forms.CharField(label='Ваш пароль: ',
                               widget=forms.PasswordInput(attrs={'class': 'form-control'}))

    def clean_email(self):
        email = self.cleaned_data['email']
        if not JobseekerRegisterInfo.objects.filter(email=email):
            raise forms.ValidationError('Неправильно введені email або пароль')
        return email

登录视图

def jobseeker_login_view(request):
    title = 'Авторизація'
    context = {'title': title}
    if request.method == 'POST':
        form = JobseekerLoginForm(request.POST)
        context['form'] = form
        if form.is_valid():
            email = form.cleaned_data['email']
            password = form.cleaned_data['password']
            user = authenticate(request, email=email, password=password)
            print(generate_password_hash(password))
            if user:
                print(user)
            else:
                print('USER IS NONE')
        else:
            form_errors = form.errors.as_data()
            custom_error = custom_error_service(form_errors)
            context['list_first_error'] = custom_error
    else:
        form = JobseekerLoginForm()
        context['form'] = form
    return render(request, template_name='jobseeker/jobseeker_login.html', context=context)

问题分析

  1. REQUIRED_FIELDS配置错误:
    该字段是创建超级用户时Django命令行要求补充的字段,不能包含hashed_password——AbstractUser自带password字段,且Django会自动处理密码哈希,无需手动添加。

  2. 原有字段处理不当:
    直接将username = first_name = last_name = None的写法不符合Django模型规范,会导致字段映射异常。AbstractUser的username默认是唯一约束,需要显式覆盖解除该约束,避免与email作为唯一标识冲突。

  3. 密码哈希不匹配:
    注册时若未使用Django内置的create_user或set_password方法处理密码,而是手动用自定义的generate_password_hash哈希密码,会导致authenticate验证失败——Django的密码验证依赖特定的哈希格式(如pbkdf2_sha256$...前缀),自定义哈希无法被识别。

修复步骤

1. 修正自定义用户模型

from django.contrib.auth.models import AbstractUser
from django.db import models
from phonenumber_field.modelfields import PhoneNumberField

class JobseekerRegisterInfo(AbstractUser):
    # 显式覆盖username字段,解除唯一约束并设为可选
    username = models.CharField(max_length=150, unique=False, blank=True, null=True)
    # 移除不需要的字段(设为可选)
    first_name = models.CharField(max_length=150, blank=True, null=True)
    last_name = models.CharField(max_length=150, blank=True, null=True)
    
    id = models.BigAutoField(primary_key=True)
    phone_number = PhoneNumberField()
    email = models.EmailField(unique=True)
    full_name = models.CharField(max_length=120)

    USERNAME_FIELD = 'email'
    # 移除hashed_password,保留必要字段
    REQUIRED_FIELDS = ['phone_number', 'full_name']

    def __str__(self):
        return self.full_name

2. 确保注册流程正确处理密码

注册时必须使用Django提供的方法创建用户,示例如下:

def jobseeker_register_view(request):
    if request.method == 'POST':
        # 假设表单获取到对应字段
        email = request.POST.get('email')
        password = request.POST.get('password')
        phone_number = request.POST.get('phone_number')
        full_name = request.POST.get('full_name')
        
        # 使用create_user自动处理密码哈希
        user = JobseekerRegisterInfo.objects.create_user(
            email=email,
            password=password,
            phone_number=phone_number,
            full_name=full_name
        )
        # 后续逻辑...

若需手动设置密码,调用user.set_password(password)后再user.save(),不要直接赋值user.password = xxx。

3. 优化登录视图与表单

  • 移除表单中clean_email的用户存在校验:authenticate会自动处理用户不存在或密码错误的情况,且该校验会泄露用户是否存在的信息。
  • 确认authenticate调用参数正确(因USERNAME_FIELD设为email,两种传参方式都有效):
# 方式一:直接传email参数
user = authenticate(request, email=email, password=password)
# 方式二:用username参数匹配USERNAME_FIELD
user = authenticate(request, username=email, password=password)

4. 验证密码哈希格式

查看数据库中password字段的值,正确格式应为类似pbkdf2_sha256$600000$...的字符串,若为自定义哈希值,需重新注册用户确保密码被Django正确哈希。

最终验证

修改完成后,重新迁移数据库(若模型有变更),注册新用户后尝试登录,authenticate应能正确返回用户实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 03:05:48