热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析Django中用户模型的自定义方法与技巧

django的内置user给我们提供了很多的方便,但是也有很多场景我们无法使用其自带的user来完全满足我们的需求,这就需要进行自定制。 首先我们来看一下django自带的user包含了哪些字段:us

django的内置user给我们提供了很多的方便,但是也有很多场景我们无法使用其自带的user来完全满足我们的需求,这就需要进行自定制。
首先我们来看一下django自带的user包含了哪些字段:

  1. username: 用户名。150个字符以内。可以包含数字和英文字符,以及_、@、+、.和-字符。不能为空,且必须唯一!
  2. first_name:在30个字符以内。可以为空。
  3. last_name:在150个字符以内。可以为空。
  4. email:邮箱。可以为空。
  5. password:密码。经过哈希过后的密码。
  6. groups:分组。一个用户可以属于多个分组,一个分组可以拥有多个用户。groups这个字段是跟Group的一个多对多的关系。
  7. user_permissions:权限。一个用户可以拥有多个权限,一个权限可以被多个用户所有用。和Permission属于一种多对多的关系。
  8. is_staff:是否可以进入到admin的站点。代表是否是员工。
  9. is_active:是否是可用的。对于一些想要删除账号的数据,我们设置这个值为False就可以了,而不是真正的从数据库中删除。
  10. is_superuser:是否是超级管理员。如果是超级管理员,那么拥有整个网站的所有权限。
  11. last_login:上次登录的时间。
  12. date_joined:账号创建的时间。

显然在越来越多的场景下,这些字段已经不能满足我们的需求,在自定制化的过程中,主要分为两个方向:扩充和改写。

1.扩展user

1.1 通过OneToOneField扩展

如果我们对于用户验证没有修改的需求,只是想给用户增加一些字段,就可以采用一对一外键的方法。增加一张表来储存站点用户的非身份验证信息。
例如可以建立一个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

1.2 通过代理模型扩展

通过集成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这个模型中获取所有的数据。

2 改写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 如何解决该循环依赖关系。

2.1 通过继承AbstractUser来改写:

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)

2.2 继承AbstractBaseUser重写User

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’的子类。

使用:

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

    # 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

  2. 重新定义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)

  3. 在创建了新的User模型后,还需要在settings中配置好。配置AUTH_USER_MODEL=‘appname.User’。


    # settings.py
    AUTH_USER_MODEL = 'youappname.User'

  4. 如何使用这个自定义的模型:比如以后我们有一个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


推荐阅读
  • Redis:缓存与内存数据库详解
    本文介绍了数据库的基本分类,重点探讨了关系型与非关系型数据库的区别,并详细解析了Redis作为非关系型数据库的特点、工作模式、优点及持久化机制。 ... [详细]
  • 流处理中的计数挑战与解决方案
    本文探讨了在流处理中进行计数的各种技术和挑战,并基于作者在2016年圣何塞举行的Hadoop World大会上的演讲进行了深入分析。文章不仅介绍了传统批处理和Lambda架构的局限性,还详细探讨了流处理架构的优势及其在现代大数据应用中的重要作用。 ... [详细]
  • 本文详细介绍了如何使用Heartbeat构建一个高可用性的Apache集群,包括安装、配置和测试步骤。 ... [详细]
  • 利用 Calcurse 在 Linux 终端高效管理日程与任务
    对于喜爱使用 Linux 终端进行日常操作的系统管理员来说,Calcurse 提供了一种强大的方式来管理日程安排、待办事项及会议。本文将详细介绍如何在 Linux 上安装和使用 Calcurse,帮助用户更有效地组织工作。 ... [详细]
  • OBS Studio自动化实践:利用脚本批量生成录制场景
    本文探讨了如何利用OBS Studio进行高效录屏,并通过脚本实现场景的自动生成。适合对自动化办公感兴趣的读者。 ... [详细]
  • 如何在PHP中安装Xdebug扩展
    本文介绍了如何从PECL下载并编译安装Xdebug扩展,以及如何配置PHP和PHPStorm以启用调试功能。 ... [详细]
  • 本文探讨了在一个物理隔离的环境中构建数据交换平台所面临的挑战,包括但不限于数据加密、传输监控及确保文件交换的安全性和可靠性。同时,作者结合自身项目经验,分享了项目规划、实施过程中的关键决策及其背后的思考。 ... [详细]
  • 实践指南:使用Express、Create React App与MongoDB搭建React开发环境
    本文详细介绍了如何利用Express、Create React App和MongoDB构建一个高效的React应用开发环境,旨在为开发者提供一套完整的解决方案,包括环境搭建、数据模拟及前后端交互。 ... [详细]
  • 本文介绍了如何利用X_CORBA实现远程对象调用,并通过多个示例程序展示了其功能与应用,包括基础的Hello World示例、文件传输工具以及一个完整的聊天系统。 ... [详细]
  • 深入解析:存储技术的演变与发展
    本文探讨了从单机文件系统到分布式文件系统的存储技术发展过程,详细解释了各种存储模型及其特点。 ... [详细]
  • 协程作为一种并发设计模式,能有效简化Android平台上的异步代码处理。自Kotlin 1.3版本引入协程以来,这一特性基于其他语言的成熟理念,为开发者提供了新的工具,以增强应用的响应性和效率。 ... [详细]
  • 本文探讨了一种统一的语义数据模型,旨在支持物联网、建筑及企业环境下的数据转换。该模型强调简洁性和可扩展性,以促进不同行业间的插件化和互操作性。对于智能硬件开发者而言,这一模型提供了重要的参考价值。 ... [详细]
  • 所在位置|室友_Python+OpenCv实现图像边缘检测(滑动调节阈值)
    所在位置|室友_Python+OpenCv实现图像边缘检测(滑动调节阈值) ... [详细]
  • 电商高并发解决方案详解
    本文以京东为例,详细探讨了电商中常见的高并发解决方案,包括多级缓存和Nginx限流技术,旨在帮助读者更好地理解和应用这些技术。 ... [详细]
  • 5分钟快速筛选特定候选人的简历
    介绍一个新的系列,专注于提供能在5分钟内完成的简单实用案例。本文将以办公中常见的需求为例,展示如何快速从大量简历中筛选出特定候选人的简历。 ... [详细]
author-avatar
Superficial1987542_y3
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有