django的内置user给我们提供了很多的方便,但是也有很多场景我们无法使用其自带的user来完全满足我们的需求,这就需要进行自定制。
首先我们来看一下django自带的user包含了哪些字段:
显然在越来越多的场景下,这些字段已经不能满足我们的需求,在自定制化的过程中,主要分为两个方向:扩充和改写。
如果我们对于用户验证没有修改的需求,只是想给用户增加一些字段,就可以采用一对一外键的方法。增加一张表来储存站点用户的非身份验证信息。
例如可以建立一个Employee模型:
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)
假设一个既有用户又有雇员模型的现有雇员Fred Smith,您可以使用Django的标准相关模型约定访问相关信息:
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
通过集成Uesr,来扩展User的方法,不改变表结构,只是增加方法。
例:
# 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)
在Meta中设置proxy=True,说明这个只是User的一个代理模型。他并不会影响原来User模型在数据库中表的结构。以后如果你想方便的获取所有黑名单的人,那么你就可以通过Person.get_blacklist()就可以获取到。并且User.objects.all()和Person.objects.all()其实是等价的。因为他们都是从User这个模型中获取所有的数据。
Django内置的 类:~django.contrib.auth.models.User
模型 可能并不适合一些项目的身份验证需求。例如,在一些网站上使用邮件地址代替用户名来作为你的标识令牌更有意义。
Django 允许你为引用了自定模型的:setting: AUTH_USER_MODEL 设置一个值来重写默认的用户表。
#settings.py
AUTH_USER_MODEL = 'myapp.MyUser'
如果你准备启动一个新的项目,强烈推荐你设置一个自定义的用户模型,即使默认的用户模型对你来说已经足够了。这个模型的行为与默认用户模型相通,但是你能在未来需要的时候自定义它:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
不要忘记将 AUTH_USER_MODEL 指向它。在创建任何迁移或者首次运行 manage.py migrate 之前执行这个操作。
同样的,在 app 中的 admin.py 中注册模型。
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)
在你已经建立数据库表之后再去修改 AUTH_USER_MODEL 要困难的多,因为它会影响外键和多对多关系。
这个改动并不能自动完成,需要手动修复你的架构,将数据从旧的用户表移出,并有可能需要手动执行一些迁移操作。查看步骤概述,请查看 #25313 。
由于Django针对可交换模型的动态依赖特性的限制,被 AUTH_USER_MODEL 引用的模型必须在第一次迁移的时候创建(通常被称作0001_initial
);否则,你将会遇到依赖问题。
此外,在运行迁移时可能会遇到 CircularDependencyError ,因为Django由于动态依赖性而无法自动中断依赖循环。如果你遇到这个错误,则应通过移除依赖用户模型的其他模型,并进行二次迁移。(如果你想了解它通常是如何运行的,可以尝试建立两个相互指向彼此的外键的普通模型,并查看 makemigrations 如何解决该循环依赖关系。
django.contrib.auth.models.User也有继承这个类,使用继承AbstractUser来改写,可以在原有的User类中增加一些字段,同时也可以自己定义帐号身份校验的相关内容。需要进行migrate。
例如我们想要增加字段和重写校验:
# 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)
AbstractBaseUser提供了User类最核心的实现,包括哈希的passwords和 标识的密码重置。
以下是一些该类的重要参数:
USERNAME_FIELD
作为唯一标识符的描述用户模型字段名的字符串,通常是一个用户名,但也可以是一个电子邮件地址,或任何其他唯一标识符。该字段必须是唯一的(即定义了 unique=True ),除非你使用自定义身份验证后端,可以支持非唯一的用户名。接下来的样例中,identifier 字段将被用作识别字段。
class MyUser(AbstractBaseUser):
identifier = models.CharField(max_length=40, unique=True)
...
USERNAME_FIELD = 'identifier'
EMAIL_FIELD
用来描述用户模型中的邮件字段,该值通过 get_email_field_name() 返回。
REQUIRED_FIELDS
当通过命令行 createsuperuser 来创建用户时提示的必填字段列表。这个列表里的字段必须是非空或者未定义字段,也可以包含一些你想在创建用户时进行提示的附加字段。 REQUIRED_FIELDS 对Django的其他部分无效,比如在admin页面中创建用户。
比如说,这里是一个局部的用户模型,定义了两个必须的字段——生日和身高。
class MyUser(AbstractBaseUser):
...
date_of_birth = models.DateField()
height = models.FloatField()
...
REQUIRED_FIELDS = ['date_of_birth', 'height']
REQUIRED_FIELDS 必须包含你的用户模型中所有的必填字段,但不用包含
USERNAME_FIELD
或 password
,因为这些字段一直都会被提示。
is_active
一个布尔属性,指明用户是否被“激活”。这个属性作为 AbstractBaseUser 的属性提供,默认是 True 。如何去实现该属性的功能取决于你所选择的认证后端。
get_full_name()
可选项。用户的较长身份标识符,比如用户的全名。如果已经设置,则会与用户名一起出现在 django.contrib.admin 中。
get_short_name()
可选项。用户较短的身份标识符,比如用户的名。如果已经设置,它会在 django.contrib.admin 页面头部的欢迎词中替换用户名。
AbstractBaseUser 的任何子类都可以使用下面的属性和方法:
get_username()
返回 USERNAME_FIELD 指定的字段的值。
clean()
通过调用 normalize_username() 来规范化用户名。 如果重写此方法,必须调用 super() 来保持规范化。
classmethod get_email_field_name()
返回由 EMAIL_FIELD 属性指定的电子邮件字段的名称。 如果未指定 EMAIL_FIELD ,则默认为 ‘email’ 。
classmethod normalize_username(username)
应用NFKC Unicode 规范化用户名,使得不同Unicode码位视觉相同字符视为相同。
is_authenticated
只读属性,始终返回 True (匿名用户 AnonymousUser.is_authenticated 始终返回 False )。这是一种判断用户是否已通过身份验证的方法。这并不意味着任何权限,也不会检查用户是否处于活动状态或是否具有有效会话。即使通常您会根据 request.user 检查这个属性,以确定它是否被 AuthenticationMiddleware 填充(表示当前登录的用户),但是你应该知道该属性对于任何 User 实例都返回True。
is_anonymous
只读属性总是’False’。这个属性用于区分类:model.User和model.AnonymousUser对象。通常情况下,属性:'model.User.is_authenticated’应该置于只读。
set_password(raw_password)
设置用户密码,谨慎保存密码哈希。不可保存类’django.conrtib.auth.models.AbstractBaseUser’的对象。
如果密码为空,密码应设置为不可用密码。例如可以使用方法:django.contrib.auth.mode.Is.AbstractBaseUser.set_unusable_password()。
check_password(raw_password)
如果密码正确则返回’True’。(密码哈希值用于比较)
set_unusable_password()
将用户标记为没有设置密码。 这与密码使用空白字符串不同。 check_password() 此用户将永远不会返回True。 不保存 AbstractBaseUser 对象。
如果针对现有外部源(例如LDAP目录)进行应用程序的身份验证,则可能需要这样做。
has_usable_password()
如果方法’django.contrib.auth.models.AbstractBaseUser.set_unusable_password()‘被调用则返回’False’。
get_session_auth_hash()
返回密码字段的HMAC。用于密码更改后会话失效。
类:'models.AbstractUser是类:‘models.AbstractBaseUser’的子类。
使用:
创建模型。示例代码如下:
# 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
重新定义UserManager:我们还需要定义自己的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)
参考文章:
https://blog.csdn.net/qq_37975685/article/details/81867334