Django User模型详解与四种扩展重写方案对比

Aria
Aria
发布时间:2026-10-13 | 6人浏览过
原创

本文深入解析Django内置User模型的字段与验证机制,并详细对比四种扩展用户模型的方法:Proxy模型、一对一外键、继承AbstractUser及继承AbstractBaseUser。重点说明各方案的适用场景、代码实现及settings配置,帮助开发者根据需求选择最优的用户管理策略。

Django User模型详解与四种扩展重写方案对比

User模型

User模型是Django框架的核心组成部分,其完整路径为django.contrib.auth.models.User。以下对用户对象进行简要介绍:

User模型字段解析

字段:

内置的User模型包含以下字段:

username:用户名,限制在150个字符以内。允许包含数字、英文字符以及_、@、+、.和-符号。该字段不能为空且必须唯一。

first_name:名,限制在30个字符以内。可以为空。

last_name:姓,限制在150个字符以内。可以为空。

email:邮箱地址。可以为空。

password:密码,存储的是经过哈希处理后的值。

groups:分组。一个用户可以属于多个分组,一个分组也可以包含多个用户。该字段与Group模型建立多对多关系。

user_permissions:权限。一个用户可以拥有多个权限,一个权限也可以被多个用户共享。该字段与Permission模型建立多对多关系。

is_staff:标识用户是否可以访问Admin站点,即是否为员工。

is_active:标识用户是否可用。对于需要“删除”的账号,可将此值设为False,而非直接从数据库中物理删除。

is_superuser:标识是否为超级管理员。如果是,则拥有网站的所有权限。

last_login:上次登录时间。

date_joined:账号创建时间。

登录验证机制

登录验证:

Django的验证系统已内置登录验证功能。通过调用django.contrib.auth.authenticate即可实现,该方法仅支持通过username和password进行验证。示例代码如下:


from django.contrib.auth import authenticate
user = authenticate(username='Tom', password='111111')
# 如果验证通过了,那么就会返回一个user对象。
if user is not None:
  # 执行验证通过后的代码
else:
  # 执行验证没有通过的代码。

扩展用户模型的四种方法

扩展用户模型:

虽然Django内置的User模型功能强大,但有时仍无法满足特定需求。例如,登录验证默认使用用户名,而实际需求可能要求通过手机号或邮箱验证;或者需要增加新的自定义字段。此时,扩展用户模型成为必要选项。扩展用户模型主要有以下几种方式,下文将逐一讨论。

Proxy代理模型

1. 设置Proxy模型:

作用:为模型增加操作方法

局限:不能增加或减少User模型的字段

好处:不破坏原来的User模型的表结构

如果你对Django提供的字段及验证方法满意,仅需在原有基础上增加操作逻辑,建议使用此方式。示例代码如下:


# models.py
class Person(User):
  # 如果模型是一个代理模型
  # 那么就不能在这个模型中添加新的Field
  # telephone = models.CharField(max_length=11) # 错误写法
  class Meta:
    proxy = True
 
  # proxy正确用法是给模型添加自定义方法
  # 如添加列出黑名单的方法
  def get_blacklist(self):
    return self.objects.filter(is_active=False)

在上述代码中,我们定义了一个Person类继承自User,并在Meta中设置proxy=True,表明这是一个代理模型,不会影响数据库中User表的实际结构。例如,若需获取所有黑名单用户,可通过Person.get_blacklist()方法实现。需要注意的是,User.objects.all()和Person.objects.all()是等价的,因为它们都从User模型中获取数据。

一对一外键扩展

2. 一对一外键:

作用:给模型增加新的字段和新方法

局限:只能增加,不能减少字段;不能修改用户验证方法(authenticate)

好处:不破坏原来的User模型的表结构

如果你不使用authenticate进行其他验证(即仅使用username和password),但希望添加新字段,可以使用一对一外键方式。示例代码如下:


# models.py
from django.contrib.auth.models import User
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save
 
class UserExtension(models.Model):
  user = models.OneToOneField(User,on_delete=models.CASCADE,related_name='extension')
  birthday = models.DateField(null=True,blank=True)
  school = models.CharField(max_length=100)
 
 
@receiver(post_save,sender=User)
def create_user_extension(sender,instance,created,**kwargs):
  if created:
    UserExtension.objects.create(user=instance)
  else:
    instance.extension.save()

上述代码定义了一个UserExtension模型,并与User模型建立一对一绑定。新增字段添加到UserExtension中。同时,通过信号处理器监听User的save方法,当User创建时,自动创建并绑定对应的UserExtension实例。


# views.py
from django.contrib.auth.models import User
from django.http import HttpResponse
 
 
def one_to_one_view(request):
  user = User.objects.create_user(username='Tom',email='tom@qq.com',password='111111')
  # 给扩展的字段设置值
  user.extension.school = 'Harvard'
  user.save()
  return HttpResponse('一对一扩展User模型')

继承AbstractUser

3. 继承自AbstractUser:

作用:给模型增加新的字段,修改用户验证方法(authenticate)

局限:只能增加,不能减少字段

坏处:破坏了原来的User模型的表结构

如果对默认的authenticate验证不满意,且希望保留原User字段的同时增加新字段(如telephone和school),可以直接继承django.contrib.auth.models.AbstractUser。注意,AbstractUser是User的父类。示例代码如下:


# models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
  telephone = models.CharField(max_length=11,unique=True)
  school = models.CharField(max_length=100)
  # 指定telephone作为USERNAME_FIELD, 而不是原来的username字段, 所以username要重写
  username = models.CharField(max_length=150)
 
  # 指定telephone作为USERNAME_FIELD,以后使用authenticate
  # 函数验证的时候,就可以根据telephone来验证
  # 而不是原来的username
  USERNAME_FIELD = 'telephone'
  # USERNAME_FIELD对应的'telephone'字段和密码字段默认是必须的字段
  # 下[]可以添加其它必须的字段, 比如['username', 'email']
  REQUIRED_FIELDS = []
 
  # 重新定义Manager对象,在创建user的时候使用telephone和
  # password,而不是使用username和password
  objects = UserManager()
 
 
# 重写UserManager
class UserManager(BaseUserManager):
  use_in_migrations = True
 
  def _create_user(self, telephone, password, **extra_fields):
    if not telephone:
      raise ValueError("请填入手机号码!")
    if not password:
      raise ValueError("请填入密码!")
    user = self.model(telephone=telephone, **extra_fields)
    user.set_password(password)
    user.save(using=self._db)
    return user
 
  def create_user(self, telephone, password, **extra_fields):
    extra_fields.setdefault('is_staff', False)
    extra_fields.setdefault('is_superuser', False)
    return self._create_user(telephone, password, **extra_fields)
 
  def create_superuser(self, telephone, password, **extra_fields):
    extra_fields.setdefault('is_staff', True)
    extra_fields.setdefault('is_superuser', True)
 
    if extra_fields.get('is_staff') is not True:
      raise ValueError('Superuser must have is_staff=True.')
    if extra_fields.get('is_superuser') is not True:
      raise ValueError('Superuser must have is_superuser=True.')
 
    return self._create_user(telephone, password, **extra_fields)

随后需在settings中进行配置。


# settings.py
AUTH_USER_MODEL = 'youappname.User'

由于此方式破坏了原有的User表结构,必须在执行第一次migrate之前定义好模型。

继承AbstractBaseUser

4. 继承自AbstractBaseUser模型:

作用:给模型增加或减少字段,修改用户验证方法(authenticate)

坏处:破坏了原来的User模型的表结构

注意:继承自AbstractBaseUser的同时还需继承PermissionsMixin

若需修改默认验证方式,且希望移除原User模型中不需要的字段,可自定义模型并继承AbstractBaseUser。此方式较为复杂,建议对Django有深入了解后再使用。步骤如下:

创建模型。示例代码如下:


# models.py
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.db import models
 
 
class User(AbstractBaseUser,PermissionsMixin):
   email = models.EmailField(unique=True)
   username = models.CharField(max_length=150)
   telephone = models.CharField(max_length=11,unique=True)
   is_staff = models.BooleanField(default=False)
   is_active = models.BooleanField(default=True)
 
   USERNAME_FIELD = 'telephone'
   REQUIRED_FIELDS = []
 
  # 这里的UserManager同方法3, 需要重写
   objects = UserManager()
 
   def get_full_name(self):
     return self.username
 
   def get_short_name(self):
     return self.username

其中password和last_login字段已在AbstractBaseUser中定义,直接继承即可。接着添加所需字段,如email、username、telephone等。为了尽可能模拟User模型的行为,需注意以下几点:

USERNAME_FIELD:用于指定User模型中作为唯一标识的字段名。若未修改,默认使用username。

REQUIRED_FIELDS:字段名列表,用于createsuperuser命令创建用户时的提示字段。

is_active:布尔值,标识用户当前是否可用。

get_full_name():获取完整姓名。

get_short_name():获取简短姓名。

重新定义UserManager:由于默认UserManager创建用户时使用username和password,需替换为telephone。示例代码如下:


# models.py
from django.contrib.auth.base_user import BaseUserManager
 
 
# 重写UserManager
class UserManager(BaseUserManager):
  use_in_migrations = True
 
  def _create_user(self, telephone, password, **extra_fields):
    if not telephone:
      raise ValueError("请填入手机号码!")
    if not password:
      raise ValueError("请填入密码!")
    user = self.model(telephone=telephone, **extra_fields)
    user.set_password(password)
    user.save(using=self._db)
    return user
 
  def create_user(self, telephone, password, **extra_fields):
    extra_fields.setdefault('is_staff', False)
    extra_fields.setdefault('is_superuser', False)
    return self._create_user(telephone, password, **extra_fields)
 
  def create_superuser(self, telephone, password, **extra_fields):
    extra_fields.setdefault('is_staff', True)
    extra_fields.setdefault('is_superuser', True)
 
    if extra_fields.get('is_staff') is not True:
      raise ValueError('Superuser must have is_staff=True.')
    if extra_fields.get('is_superuser') is not True:
      raise ValueError('Superuser must have is_superuser=True.')
 
    return self._create_user(telephone, password, **extra_fields)

创建新User模型后,需在settings中配置AUTH_USER_MODEL='appname.User'。

# settings.py AUTH_USER_MODEL = 'youappname.User'

如何使用这个自定义的模型:例如,若Article模型需通过外键引用User模型,可通过以下两种方式引用。

第一种是直接导入User到当前文件。示例代码如下:


# models.py
from django.db import models
from myauth.models import User
class Article(models.Model):
  title = models.CharField(max_length=100)
  content = models.TextField()
  author = models.ForeignKey(User, on_delete=models.CASCADE)

这种方式可行,但为了提升可用性,建议将User抽象出来,使用settings.AUTH_USER_MODEL表示。示例代码如下:


# models.py
from django.db import models
from django.conf import settings
class Article(models.Model):
  title = models.CharField(max_length=100)
  content = models.TextField()
  author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

由于此方式破坏了原有的User表结构,必须在执行第一次migrate之前定义好模型。

以上是对Django User模型及四种扩展/重写方法的总结,希望能提供有益参考。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表