Django User模型详解与四种扩展重写方案对比
本文深入解析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模型及四种扩展/重写方法的总结,希望能提供有益参考。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













