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


推荐阅读
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 本文深入探讨了HTTP请求和响应对象的使用,详细介绍了如何通过响应对象向客户端发送数据、处理中文乱码问题以及常见的HTTP状态码。此外,还涵盖了文件下载、请求重定向、请求转发等高级功能。 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 探讨如何从数据库中按分组获取最大N条记录的方法,并分享新年祝福。本文提供多种解决方案,适用于不同数据库系统,如MySQL、Oracle等。 ... [详细]
  • 本文介绍如何使用 Angular 6 的 HttpClient 模块来获取 HTTP 响应头,包括代码示例和常见问题的解决方案。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • XNA 3.0 游戏编程:从 XML 文件加载数据
    本文介绍如何在 XNA 3.0 游戏项目中从 XML 文件加载数据。我们将探讨如何将 XML 数据序列化为二进制文件,并通过内容管道加载到游戏中。此外,还会涉及自定义类型读取器和写入器的实现。 ... [详细]
  • 使用Vultr云服务器和Namesilo域名搭建个人网站
    本文详细介绍了如何通过Vultr云服务器和Namesilo域名搭建一个功能齐全的个人网站,包括购买、配置服务器以及绑定域名的具体步骤。文章还提供了详细的命令行操作指南,帮助读者顺利完成建站过程。 ... [详细]
  • andr ... [详细]
  • 深入理解Java泛型:JDK 5的新特性
    本文详细介绍了Java泛型的概念及其在JDK 5中的应用,通过具体代码示例解释了泛型的引入、作用和优势。同时,探讨了泛型类、泛型方法和泛型接口的实现,并深入讲解了通配符的使用。 ... [详细]
  • 根据最新发布的《互联网人才趋势报告》,尽管大量IT从业者已转向Python开发,但随着人工智能和大数据领域的迅猛发展,仍存在巨大的人才缺口。本文将详细介绍如何使用Python编写一个简单的爬虫程序,并提供完整的代码示例。 ... [详细]
  • 本文介绍如何使用Python进行文本处理,包括分词和生成词云图。通过整合多个文本文件、去除停用词并生成词云图,展示文本数据的可视化分析方法。 ... [详细]
  • 在多线程编程环境中,线程之间共享全局变量可能导致数据竞争和不一致性。为了解决这一问题,Linux提供了线程局部存储(TLS),使每个线程可以拥有独立的变量副本,确保线程间的数据隔离与安全。 ... [详细]
  • 社交网络中的级联行为 ... [详细]
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社区 版权所有