热门标签 | 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


推荐阅读
  • 在Unity3D的第13天学习中,我们深入探讨了关节系统和布料模拟技术。关节系统作为Unity中的关键物理组件,能够实现游戏对象间的动态连接,如刚体间的关系、门的开合动作以及角色的布娃娃效果。铰链关节涉及两个刚体的交互,能够精确模拟复杂的机械运动,为游戏增添了真实感。此外,布料模拟技术则进一步提升了角色衣物和环境装饰物的自然表现,增强了视觉效果的真实性和沉浸感。 ... [详细]
  • 本文深入解析了 Apache 配置文件 `httpd.conf` 和 `.htaccess` 的优化方法,探讨了如何通过合理配置提升服务器性能和安全性。文章详细介绍了这两个文件的关键参数及其作用,并提供了实际应用中的最佳实践,帮助读者更好地理解和运用 Apache 配置。 ... [详细]
  • 本文作为“实现简易版Spring系列”的第五篇,继前文深入探讨了Spring框架的核心技术之一——控制反转(IoC)之后,将重点转向另一个关键技术——面向切面编程(AOP)。对于使用Spring框架进行开发的开发者来说,AOP是一个不可或缺的概念。了解AOP的背景及其基本原理,对于掌握这一技术至关重要。本文将通过具体示例,详细解析AOP的实现机制,帮助读者更好地理解和应用这一技术。 ... [详细]
  • NoSQL数据库,即非关系型数据库,有时也被称作Not Only SQL,是一种区别于传统关系型数据库的管理系统。这类数据库设计用于处理大规模、高并发的数据存储与查询需求,特别适用于需要快速读写大量非结构化或半结构化数据的应用场景。NoSQL数据库通过牺牲部分一致性来换取更高的可扩展性和性能,支持分布式部署,能够有效应对互联网时代的海量数据挑战。 ... [详细]
  • 本文深入探讨了 iOS 开发中 `int`、`NSInteger`、`NSUInteger` 和 `NSNumber` 的应用与区别。首先,我们将详细介绍 `NSNumber` 类型,该类用于封装基本数据类型,如整数、浮点数等,使其能够在 Objective-C 的集合类中使用。通过分析这些类型的特性和应用场景,帮助开发者更好地理解和选择合适的数据类型,提高代码的健壮性和可维护性。苹果官方文档提供了更多详细信息,可供进一步参考。 ... [详细]
  • 从零起步:使用IntelliJ IDEA搭建Spring Boot应用的详细指南
    从零起步:使用IntelliJ IDEA搭建Spring Boot应用的详细指南 ... [详细]
  • 基于Node.js的高性能实时消息推送系统通过集成Socket.IO和Express框架,实现了高效的高并发消息转发功能。该系统能够支持大量用户同时在线,并确保消息的实时性和可靠性,适用于需要即时通信的应用场景。 ... [详细]
  • Spring Boot 实战(一):基础的CRUD操作详解
    在《Spring Boot 实战(一)》中,详细介绍了基础的CRUD操作,涵盖创建、读取、更新和删除等核心功能,适合初学者快速掌握Spring Boot框架的应用开发技巧。 ... [详细]
  • 期末Web开发综合实践项目:运用前端技术打造趣味小游戏体验
    期末Web开发综合实践项目中,学生通过运用HTML、CSS和JavaScript等前端技术,设计并实现了一款趣味性十足的小游戏。该项目不仅检验了学生对前端基础知识的掌握情况,还提升了他们的实际操作能力和创意设计水平。视频链接展示了项目的最终成果,直观呈现了游戏的互动性和视觉效果。 ... [详细]
  • 在稀疏直接法视觉里程计中,通过优化特征点并采用基于光度误差最小化的灰度图像线性插值技术,提高了定位精度。该方法通过对空间点的非齐次和齐次表示进行处理,利用RGB-D传感器获取的3D坐标信息,在两帧图像之间实现精确匹配,有效减少了光度误差,提升了系统的鲁棒性和稳定性。 ... [详细]
  • 在处理大规模并发请求时,传统的多线程或多进程模型往往无法有效解决性能瓶颈问题。尽管它们在处理小规模任务时能提升效率,但在高并发场景下,系统资源的过度消耗和上下文切换的开销会显著降低整体性能。相比之下,Python 的 `asyncio` 模块通过协程提供了一种轻量级且高效的并发解决方案。本文将深入解析 `asyncio` 模块的原理及其在实际应用中的优化技巧,帮助开发者更好地利用协程技术提升程序性能。 ... [详细]
  • voc生成xml 代码
    目录 lxmlwindows安装 读取示例 可视化 生成示例 上面是代码,下面有调用示例 api调用代码,其实只有几行:这个生成代码也很简 ... [详细]
  • 深入理解Spark框架:RDD核心概念与操作详解
    RDD是Spark框架的核心计算模型,全称为弹性分布式数据集(Resilient Distributed Dataset)。本文详细解析了RDD的基本概念、特性及其在Spark中的关键操作,包括创建、转换和行动操作等,帮助读者深入理解Spark的工作原理和优化策略。通过具体示例和代码片段,进一步阐述了如何高效利用RDD进行大数据处理。 ... [详细]
  • 欢迎来到Netgen新时代:探索网络生成技术的无限可能
    欢迎进入Netgen的新时代:探索网络生成技术的无限潜力。本文将详细介绍如何编译下载的Netgen源代码,生成Netgen程序,并提供开发所需的库nglib。此外,还将探讨Netgen在现代网络设计与仿真中的应用前景,以及其在提高网络性能和可靠性方面的关键作用。 ... [详细]
  • 在启用分层编译的情况下,即时编译器(JIT)的触发条件涉及多个因素,包括方法调用频率、代码复杂度和运行时性能数据。本文将详细解析这些条件,并探讨分层编译如何优化JVM的执行效率。 ... [详细]
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社区 版权所有