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

Django切换邮箱为默认登录报username未知字段报错原因咨询

问题背景

使用Django自定义用户模型(将登录标识从用户名改为邮箱)时,启动Admin后台触发如下报错:

Unknown field(s) (username) specified for Profile. Check fields/fieldsets/exclude attributes of class CustomUserAdmin.

涉及的原有代码如下:

admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import UserProfileForm
from .models import Profile

class CustomUserAdmin(UserAdmin):
    add_form = UserProfileForm
    model = Profile
    list_display=('id','email','password','first_name','last_name','phone_number','is_edit','team')
    ordering = ('email',)
    pass

admin.site.register(Profile, CustomUserAdmin)
##Maybe rework this later for better inclusion of all models.
from django.apps import apps
from django.contrib import admin

class ListAdminMixin(object):
    def __init__(self, model, admin_site):
        self.list_display = [field.name for field in model._meta.fields]
        super(ListAdminMixin, self).__init__(model, admin_site)

models = apps.get_models()
for model in models:
    admin_class = type('AdminClass', (ListAdminMixin, admin.ModelAdmin), {})
    try:
        admin.site.register(model, admin_class)
    except admin.sites.AlreadyRegistered:
        pass

models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
from phonenumber_field.modelfields import PhoneNumberField
import uuid
from .managers import CustomUserManager
class Team(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

class Profile(AbstractUser):
    username = None
    email = models.EmailField('email address', unique=True) # Here
    first_name = models.TextField(max_length=100, blank=True,null=True)
    last_name = models.TextField(max_length=100, blank=True,null=True)
    phone_number = PhoneNumberField(max_length=25, region='US')
    is_edit = models.BooleanField(default=False)
    team = models.ForeignKey(Team,on_delete=models.CASCADE,null=True, blank=True, default = None)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = CustomUserManager()

    def __str__(self):
        return self.email
报错根因

报错核心是父子类配置不匹配:

  • 你写的CustomUserAdmin继承了Django内置的UserAdmin,这个父类是专门给默认带username字段的用户模型设计的,它默认的fieldsets、add_fieldsets(控制后台编辑页字段分组、展示规则的核心配置)里,硬编码写死了要展示username字段。
  • 但你的Profile模型继承AbstractUser之后,直接写了username = None,把模型自带的username字段彻底移除了,还把登录标识USERNAME_FIELD改成了邮箱。
  • Admin站点启动注册Profile模型的时候,会按父类UserAdmin的默认配置挨个检查字段是否在模型上真实存在,查到username时发现模型根本没有对应字段,直接抛出错误。
  • 后面那段遍历所有模型自动注册的代码不会触发这个问题,因为代码里做了重复注册的异常捕获,Profile已经提前注册完成,不会走通用注册逻辑。
readonly_fields=('username',)的生效逻辑

添加这个配置能消除报错,本质是利用了Django Admin的字段校验规则,属于临时取巧的方案,并不是标准修复方式:

  • Django Admin初始化ModelAdmin配置时,只会对可编辑字段做强制存在性校验:所有配置在fields/fieldsets/add_fieldsets/list_editable里的可编辑字段,必须是模型上真实定义的字段,缺一个就会抛出字段不存在的错误。
  • 一旦字段被加入readonly_fields列表,校验逻辑就会跳过对这个字段的强校验:Admin会默认这个字段要么是模型上的只读字段,要么是开发者在ModelAdmin里自定义的展示方法/属性,不需要对应模型里的可写字段,因此不会触发username不存在的报错。
  • 这种修复方式有明显副作用:你的Profile模型里根本没有username字段,后台打开用户编辑页时,原用户名的展示位置只会是空值,而且整个页面的字段分组还是Django默认给用户名登录场景设计的结构,和你现在用的邮箱登录逻辑完全不匹配。
标准修复方案

正确做法是重写CustomUserAdmin的字段配置,把所有和username相关的配置全部移除,替换为你自定义模型里的实际字段,参考代码如下:

class CustomUserAdmin(UserAdmin):
    add_form = UserProfileForm
    model = Profile
    list_display = ('id','email','first_name','last_name','phone_number','is_edit','team')
    ordering = ('email',)
    # 重写编辑页字段集,移除所有username相关配置
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('个人信息', {'fields': ('first_name', 'last_name', 'phone_number', 'team', 'is_edit')}),
        ('权限配置', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
        ('时间记录', {'fields': ('last_login', 'date_joined')}),
    )
    # 重写新增用户页字段集
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active'),
        }),
    )

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 16:45:45