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)
问题分析
REQUIRED_FIELDS配置错误:
该字段是创建超级用户时Django命令行要求补充的字段,不能包含hashed_password——AbstractUser自带password字段,且Django会自动处理密码哈希,无需手动添加。原有字段处理不当:
直接将username = first_name = last_name = None的写法不符合Django模型规范,会导致字段映射异常。AbstractUser的username默认是唯一约束,需要显式覆盖解除该约束,避免与email作为唯一标识冲突。密码哈希不匹配:
注册时若未使用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

