django自定义用户模型
前言
最近写过几个小的django项目,想要直接使用django内置的用户管理功能模块来实现用户的登录和区别不同用户做数据展示。在网上翻看了不少文章,讲的都是去继承和重写AbstractUse或者AbstractBaseUser类。看完之后不能说没用,但是总有一种雾里看花的感觉。所以我又找了django官方文档看了一下,在这里按自己的理解做一个简单的介绍。
我们能想到使用django内置的用户管理肯定就不只是想要在原有的用户表里加几个字段了,django还内置的有权限管理、用户分组等等,全套拿过来用不香吗?
所以就不能光知道个User模型了,深入一点的还有UserManager管理器模型,Permission模型,Group模型等等。再深入一点还可以自定义数据增删改查以外的权限、自定义认证后端等等。
本文主要围绕django内置的User模型和UserManager模型展开介绍,其他内容读者可以自行查阅django官方文档,下附官方文档链接:
https://docs.djangoproject.com/zh-hans/4.2/ref/contrib/auth/
django内置User模型的简单使用
在前言的官方文档链接中有详细介绍User模型的所有字段、属性和方法,点击查看自定义用户模型。如果只是简单使用User模型的话,官方给出了两种方法:
-
基于User创建一个代理模型,注意,这种方式只适用于纯粹的行为改变,不能改变原有的表字段结构
# models.py from django.contrib.auth.models import User # 继承User模型获取所有字段、属性、方法 class Person(User): class Meta: proxy = True # 指明当前类是一个代理模型 # 重写get_full_name()方法 def get_full_name(self): return f"{self.last_name}{self.first_name}"
-
在User模型之外额外写一个模型,并添加一对一外键字段关联到User模型。这样一来,新的模型就是对原有User模型字段的简单补充,但是新增的模型只是一个普通的模型,django框架在执行内置的认证相关功能时是不会去查看这些补充信息的
# models.py from django.contrib.auth.models import User from django.db import models # 创建一个user补充信息的模型 class UserPhone(models.Model): user = models.OneToOneField(User,on_delete=models.CASCADE,verbose_name="用户") phone = models.CharField("联系电话",max_length=11)
# admin.py from django.contrib import admin from django.contrib.admin import TabularInline from django.contrib.auth.admin import UserAdmin as OldUserAdmin from django.contrib.auth.models import User from .models import UserPhone # 可以在外键所指向的模型中添加当前模型对象的嵌套表单 class UserPhonInline(TabularInline): model = UserPhon extra = 0 # 重写原有的UserAdmin模型,添加嵌套表单 class UserAdmin(OldUserAdmin): inlines = [UserPhonInline] # 重新注册UserAdmin模型 admin.site.unregister(User) admin.site.register(User,UserAdmin)
自定义用户模型
当你发现上述简单的使用django内置的User模型字段已经不能满足你的业务需求,想要自定义一个用户模型来替换内置的User模型,让django内置的其他相关功能都从衔接原有的User模型改成衔接你自定义的模型时,就是现在网上能找到的绝大多数文章讲述的内容。
主要分两个步骤来实现:
- 继承并重写用户模型
- 在settings.py文件中指定自定你自己的模型,表示用你自己的模型覆盖django内置的用户模型
先来看内置User模型的继承关系:
class AbstractBaseUser(models.Model):
"""
……
"""
class PermissionsMixin(models.Model):
"""
Add the fields and methods necessary to support the Group and Permission
models using the ModelBackend.
"""
class AbstractUser(AbstractBaseUser,PermissionMixin):
"""
An abstract base class implementing a fully featured User model with
admin-compliant permissions.
Username and password are required. Other fields are optional.
"""
class User(AbstractUser):
"""
Users within the Django authentication system are represented by this
model.
Username and password are required. Other fields are optional.
"""
这些东西都可以通过Ctrl键加鼠标左击查看源码的方式看到,所以我简单复制了一下就没有过多的展示。
- AbstractBaseUser类中只有密码和上一次登录时间两个字段
- PermissionsMixin主要是用来定义分组和权限相关内容的,是否为超级用户、所属组(多对多关联Group模型)、用户权限(多对多关联Permission模型)就是这个类的主要内容
- AbstractUser继承前二者创建了一个抽象基类,我们现在看到的django首次数据迁移生成的auth_user表中的所有字段在这个类中都已经添加完成
- User类只是继承了AbstractUser类然后将自身设为django默认的用户模型
所以我们如果要自定义用户模型的话,要么继承AbstractUser在添加自己所需要的字段,要么同时继承AbstractBaseUser和PermissionMixin类所有的字段都自己定义。在字段的定义和设计上,这两种方法几乎是没有差别的,唯一的区别就是通过后一种方式创建模型时,需要指定用户管理器和标识字段等。
# models.py
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import PermissionMixin,UserManager
class MyUser(AbstractBaseUser,PermissionMixin):
# 添加字段
username = models.CharField(
verbose_name="用户名",
max_length=150,
unique=True,
validators=[UnicodeUsernameValidator()] # 字段验证规则,允许unicode字符集
)
phone = models.CharField(
verbose_name="联系电话",
max_length=15,
unique=True,
)
email = models.EmailField(_("email address"),null=True,blank=True)
is_staff = models.BooleanField("工作人员状态",default=True)
is_active = models.BooleanField("激活状态",default=True)
# 添加用户管理器
objects = UserManager()
# 指定标识字段,该字段值将作为用户登录时的账号,这个字段必须是非空且不可重复的
USERNAME_FIELD = "username"
# 邮箱字段,暂时没发现啥用,应该是后期扩展做邮件推送时用的
EMAIL_FIELD = "email"
# 指定必填字段,主要是限制createsuperuser命令时的必填字段,应当包括所有blank参数为False(默认)的字段
REQUIRED_FIELD = ["username","phone"]
如果是继承自AbstractUser类,像用户管理器、标识字段等等都是已经在AbstractUser类中配置好了,写不写都可以,无非就是继承下来之后要不要重写的问题,根据自己的实际业务逻辑来就行。用户管理器下面会详细讲,这里先不赘述。
自定义用户模型写好之后,就是告诉django框架,要用我们自己写的用户模型来覆盖原本内置的模型。这一步比较简单,就是直接在settings文件中添加一个AUTH_USER_MODEL变量,把我们自己模型的路径赋值给这个变量就可以。
# settings.py
"""
……
"""
AUTH_USER_MODEL = "myapp.MyUser" # 自定义模型所在的app名称+模型名称
自定义用户管理器
在AbstractUser类源码中我们可以看到一个名为UserManager的用户管理器,在查看UserManager源码可以看到,这个用户管理器做的事情就是创建用户和超级用户以及权限认证用的。再往上查看父类BaseUserManager还有把规范邮箱地址、生成随机密码、根据用户模型的USERNAME_FIELD字段值查询用户实例。本着看不懂就不要动的原则,平常我们自定义用户管理器的时候直接继承UserManger或者BaseUserManager类,然后重写里面的创建用户和超级用户的方法就行了。
如果我们自定义的用户模型是直接继承的AbstractUser类,也就是我们自定义的用户模型拥有跟默认用户模型全部的字段,那就直接省点事,用AbstractUser类的用户管理器UserManager就可以。如果是继承AbstractBaseUser和PermissionMixin类自定义的用户模型,那就意味着默认的UserManager可能有些不合适了,只能自己从BaseUserManager类继承过来自定义一个用户管理器了。
接上面MyUser模型的例子,我在MyUser模型中定义了一个联系电话字段,账号字段为用户名,必填字段有用户名和联系电话。那么我创建的每一个用户都应该遵循这个规则。
# models.py
from django.contrib.auth.base_user import BaseUserManager
class MyUserManager(BaseUserManager):
# 指定username 和phone字段必填
def create_user(self,usename,phone,password=None):
"""
方法的内容一般就是验证参数是否合法再保存数据
"""
def create_superuser(self,usename,phone,password=None):
"""
方法的内容一般就是验证参数是否合法,将是否为超级用户的状态赋值为True,再保存数据
"""
class MyUser(AbstractBaseUser,PermissionMixin):
"""
……
"""
# 指定用户管理器
objects = MyUserManager()
文末总结
很多人在看到django内置的用户模型后都会觉得里面的first_name和last_name字段有些多余,想要自己写一个完全只有自己需要的字段的模型。就像上面例子中的代码一样,溯源到更父级的类再继承下来写,然后牵一发动全身就要再来一个用户管理器。完全没必要啊,我们程序员使用框架的意义就是在于它带给我们的便捷,啥都重新写了干脆自己造轮子好了。不想使用这些多余的字段直接设置admin页面的表单字段让他们不可见不就行了,数据库表里多两个空的字段多就多呗。