Django用户创建异常:username为DeferredAttribute对象及登录问题求助
问题解决方案
1. 修复models.py中save方法的核心错误
你当前的save方法把类属性User.phone_number赋值给了username,这就导致存储的是字段对象本身而不是手机号值。修改成实例属性即可:
from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): image = models.ImageField(upload_to='images/', null=True, blank=True) phone_number = models.CharField(max_length=11, help_text="Введите номер телефона пользователя", verbose_name="Номер телефона") class Meta(AbstractUser.Meta): pass def save(self, *args, **kwargs): # 改为实例的phone_number属性,确保取到当前用户的手机号值 if self.phone_number: self.username = self.phone_number super().save(*args, **kwargs) def __str__(self): return self.last_name
2. 重构UserCreateForm,移除不必要字段并自动处理密码
要实现无需输入username和password的需求,需要从表单中移除这两个字段,同时自动生成不可用密码(适配无密码登录场景),并添加first_name和last_name:
from django import forms from django.contrib.auth.forms import UserCreationForm from users.models import User class UploadImageForm(forms.ModelForm): class Meta: model = User fields = ['image'] class UserCreateForm(UserCreationForm): # 隐藏密码字段,由系统自动处理 password1 = forms.CharField(widget=forms.HiddenInput(), required=False) password2 = forms.CharField(widget=forms.HiddenInput(), required=False) class Meta(UserCreationForm.Meta): model = User # 只保留需要用户填写的字段 fields = ('first_name', 'last_name', 'phone_number') def save(self, commit=True): user = super().save(commit=False) # 用手机号填充username user.username = self.cleaned_data['phone_number'] # 标记用户无可用密码(适合仅手机号登录场景) user.set_unusable_password() if commit: user.save() return user
3. 修复admin.py配置,适配用户模型认证逻辑
自定义用户Admin必须继承UserAdmin而非ModelAdmin,否则会导致登录验证逻辑异常;同时更新字段配置,移除username并添加必填的个人信息字段:
from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.utils.translation import gettext_lazy as _ from .forms import UserCreateForm from .models import User @admin.register(User) class CustomUserAdmin(UserAdmin): add_form = UserCreateForm list_display = ('last_name', 'first_name', 'phone_number', 'is_staff') list_display_links = ('last_name', 'phone_number') search_fields = ('last_name', 'first_name', 'phone_number') ordering = ['last_name'] # 新增用户时的字段配置 add_fieldsets = ( ( None, { "classes": ("wide",), "fields": ("first_name", "last_name", "phone_number"), }, ), ) # 编辑用户时的字段配置 fieldsets = ( (_("Personal info"), {"fields": ("first_name", "last_name", "phone_number", 'image')}), ( _("Permissions"), { "fields": ( "is_active", "is_staff", "is_superuser", "groups", "user_permissions", ), }, ), (_("Important dates"), {"fields": ("last_login", "date_joined")}), )
4. 配置手机号登录认证后端(可选)
如果要支持直接用手机号登录,需要自定义认证后端,在users应用下创建backends.py:
from django.contrib.auth.backends import ModelBackend from .models import User class PhoneBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): try: # 将登录输入的用户名当作手机号查找用户 user = User.objects.get(phone_number=username) # 无密码用户直接通过认证,有密码用户验证密码 if user.has_usable_password() is False or user.check_password(password): return user except User.DoesNotExist: return None def get_user(self, user_id): try: return User.objects.get(pk=user_id) except User.DoesNotExist: return None
然后在settings.py中添加认证后端:
AUTHENTICATION_BACKENDS = [ 'users.backends.PhoneBackend', 'django.contrib.auth.backends.ModelBackend', # 保留默认后端,兼容用户名登录 ]
问题根源总结
- username显示异常:错误地将类字段对象赋值给
username,而非实例的手机号值; - 登录异常:Admin继承
ModelAdmin导致用户认证逻辑不兼容,加上错误的username值无法匹配登录输入。
内容的提问来源于stack exchange,提问作者Lomka
相关产品推荐
相关产品推荐

