Django内置的认证系统开箱即用,但自带的User模型只包含用户名、密码、邮箱、姓名等基础字段。真实项目中往往需要保存手机号、头像URL、用户昵称、生日、会员等级等信息,这时就必须对User模型进行扩展。扩展方式选错了,后期改动的代价会非常大,甚至需要重写整个认证流程,所以动手之前先把方案理清楚很有必要。

方案一:继承AbstractUser添加自定义字段
这是绝大多数场景下的首选方案。AbstractUser是Django提供的一个抽象基类,它已经实现了完整的用户模型逻辑,包括密码哈希、权限系统、分组管理等,我们只需要继承它并添加自己的字段即可。由于它是抽象类,不会在数据库中生成额外的一张表,所有字段会合并在一张用户表里。
下面是一个典型的例子,我们在自定义用户模型中加入了手机号、头像和昵称三个字段:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
"""自定义用户模型"""
phone = models.CharField('手机号', max_length=11, blank=True, default='')
avatar = models.URLField('头像地址', blank=True, default='')
nickname = models.CharField('昵称', max_length=30, blank=True, default='')
class Meta:
db_table = 'user'
verbose_name = '用户'
verbose_name_plural = verbose_name写好模型后,还必须在settings.py中显式告诉Django使用这个自定义模型作为认证用户:
AUTH_USER_MODEL = 'myapp.User'
注意这里的前半部分是应用名,后半部分是模型类名。这个配置必须在第一次执行python manage.py migrate之前完成。如果数据库里已经有了默认的auth_user表,再中途切换AUTH_USER_MODEL,Django会直接报错或者产生难以处理的外键冲突。这一点是新手最容易踩的坑:新建项目时,哪怕暂时不确定要加什么字段,也应该先把自定义User模型建好,给未来留出扩展余地。
方案二:继承AbstractBaseUser完全定制
如果需求更进一步,比如希望用邮箱或手机号登录而不是用户名,或者需要去掉默认的username字段,那么就需要继承AbstractBaseUser和PermissionsMixin。AbstractBaseUser只提供密码哈希和最后登录时间这些核心能力,其余字段全部由我们自己定义,自由度最高,但需要写的东西也最多。
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from django.db import models
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('邮箱不能为空')
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
return self.create_user(email, password, **extra_fields)
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField('邮箱', unique=True)
nickname = models.CharField('昵称', max_length=30)
is_active = models.BooleanField('是否启用', default=True)
is_staff = models.BooleanField('能否登录后台', default=False)
USERNAME_FIELD = 'email' # 指定登录字段
REQUIRED_FIELDS = ['nickname']
objects = UserManager()这里有几个关键点:USERNAME_FIELD指定哪个字段作为登录标识,该字段必须设置unique=True;REQUIRED_FIELDS是执行createsuperuser命令时会提示输入的字段列表;自定义的Manager必须实现create_user和create_superuser,否则命令行创建管理员会失败。
这种方案的缺点也很明显:管理后台、表单、序列化器中凡是引用username的地方都需要自己适配,比如需要继承UserAdmin并重写fieldsets和list_display。除非确实有强烈的定制需求,否则不建议一上来就用这个方案,多数项目用AbstractUser就够了。
方案三:Profile模型一对一关联
如果项目已经上线,默认User表已经在使用中,不方便改动,那么可以新建一个Profile模型,通过一对一关系挂载附加信息:
class Profile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='profile',
verbose_name='关联用户'
)
phone = models.CharField('手机号', max_length=11, blank=True, default='')
avatar = models.URLField('头像地址', blank=True, default='')
def __str__(self):
return f'{self.user.username} 的资料'访问附加字段时用user.profile.phone。为了省去手动创建Profile的麻烦,通常配合信号在用户注册时自动生成:
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)这种方案的优点是对原有系统零侵入,随时可以加上;缺点是查询用户完整信息时多一次JOIN,而且数据分散在两张表里,管理起来不如单表清爽。Django 5.0之后官方也移除了内置的get_profile机制,推荐的做法就是在模型上定义一个get_profile的便捷方法来封装查询。
后台管理与常见问题处理
自定义User模型后,如果直接注册到admin,页面可能显示不全甚至报错,正确做法是继承UserAdmin并调整字段配置:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
@admin.register(User)
class CustomUserAdmin(UserAdmin):
list_display = ('username', 'email', 'phone', 'nickname', 'is_staff')
fieldsets = UserAdmin.fieldsets + (
('扩展信息', {'fields': ('phone', 'avatar', 'nickname')}),
)
add_fieldsets = UserAdmin.add_fieldsets + (
('扩展信息', {'fields': ('phone', 'nickname')}),
)几个容易被问到的点也顺便说明一下。第一,项目中途想从默认User切换到自定义User,官方文档明确不支持,如果数据量小,可以清空迁移记录和数据库重新来;数据量大时要手工写数据迁移脚本,成本很高。第二,其他模型引用用户时,一律写settings.AUTH_USER_MODEL而不是硬编码auth.User,例如models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE),这样将来切换用户模型不会破坏外键。第三,DRF项目中记得把GENERIC_USER_MODEL对应到序列化器中,request.user拿到的就是自定义模型实例,字段可以直接访问。
总结一下选择思路:新项目且只是加字段,选AbstractUser;需要替换登录字段或深度改造认证流程,选AbstractBaseUser;老项目没法动用户表,用Profile一对一关联兜底。先把方案定对,后面的扩展工作才能顺顺利利。
Django User模型自定义字段AbstractUser修改时间:2026-09-13 13:18:39