Django(60)Django内置User模型源码分析及自定义User

Django(60)Django内置User模型源码分析及自定义User前言Django为我们提供了内置的User模型,不需要我们再额外定义用户模型,建立用户体系了。它的完整的路径是在django.contrib.auth.models.User。User模型源码分析

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

前言

Django为我们提供了内置的User模型,不需要我们再额外定义用户模型,建立用户体系了。它的完整的路径是在django.contrib.auth.models.User
 

User模型源码分析

class User(AbstractUser):
    """
    Django 身份验证系统中的用户由该模型表示

    需要用户名和密码。其他字段是可选的。
    """
    class Meta(AbstractUser.Meta):
        swappable = 'AUTH_USER_MODEL'

我们可以看到User这个类本身没干什么事情,而是继承自AbstractUser类,那么我们查看下AbstractUser的源码

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    """
    一个抽象基类实现了一个功能齐全的用户模型 符合管理员的权限。

    需要用户名和密码。 其他字段是可选的。
    """
    # 用户民校验
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
    
    # objects用户管理,里面有创建用户的方法
    objects = UserManager()

    EMAIL_FIELD = 'email'
    # 用来描述User模型名字字段的字符串,作为唯一的标识。如果没有修改,那么会使用USERNAME来作为唯一字段。
    USERNAME_FIELD = 'username'
    # 一个字段名列表,用于当通过createsuperuser管理命令创建一个用户时的提示。
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def get_full_name(self):
        """
        返回first_name和last_name,中间有个空格
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """返回用户的first_name."""
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """发送邮件给用户."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

我们可以看到AbstractUser继承自AbstractBaseUserPermissionsMixin

  • AbstractBaseUser:基础的User模型类
  • PermissionsMixin:权限类

我们先看看AbstractUser有哪些字段和方法
 

字段

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

User模型基本用法

 

创建用户

创建用户需要用到objects = UserManager()中的方法,我们点击UserManager查看源码

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, username, email, password, **extra_fields):
        """
        使用给定的用户名、电子邮件和密码创建并保存用户。
        """
        # 如果没有username则抛出异常
        if not username:
            raise ValueError('The given username must be set')
        # 标准化电子邮件,查看源码会发现是用@进行分割
        email = self.normalize_email(email)
        # 标准化用户名
        username = self.model.normalize_username(username)
        user = self.model(username=username, email=email, **extra_fields)
        # 为用户设置密码,将纯文本密码转换为用于数据库存储的哈希值
        user.set_password(password)
        # 保存用户
        user.save(using=self._db)
        return user

    def create_user(self, username, email=None, password=None, **extra_fields):
        # 设置is_staff默认值为False,is_superuser默认值为False
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(username, email, password, **extra_fields)

    def create_superuser(self, username, email, password, **extra_fields):
        # 设置is_staff默认值为True,is_superuser默认值为True
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        
        # 如果调用此方法,is_staff必须为True,否则会抛出异常
        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        # 如果调用此方法,is_superuser必须为True,否则会抛出异常
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(username, email, password, **extra_fields)

以上源码写的十分清楚相信小伙伴们也看得很清晰了,接下里我们实际操作一下

def index(request):
    user = User.objects.create_user(username="jkc", email="123@qq.com", password="123456")
    user.save()
    return HttpResponse("ok")

我们访问以上视图后,就创建了一个普通用户,查看数据库中的auth_user表,如下
Django(60)Django内置User模型源码分析及自定义User
 

创建超级用户

创建超级用户有两种方式。第一种是使用代码的方式。用代码创建超级用户跟创建普通用户非常的类似,只不过是使用create_superuser。示例代码如下:

def index(request):
    user = User.objects.create_superuser(username="jkc3", email="123@qq.com", password="123456")
    user.save()
    return HttpResponse("ok")

也可以通过命令行的方式。命令如下:

python manage.py createsuperuser

后面就会提示你输入用户名、邮箱以及密码。
 

修改密码

因为密码是需要经过加密后才能存储进去的。所以如果想要修改密码,不能直接修改password字段,而需要通过调用set_password来达到修改密码的目的。示例代码如下:

def index(request):
    user = User.objects.get(pk=1)
    user.set_password('111111')
    user.save()
    return HttpResponse("ok")

改之前密码为$1FMDwi2zsgQu$2+8/zL6ZR43oXIvIRGfK6xrWUnv2IRjdPxVaqEwqyjM=,改完之后为$u6rNdNTvLbEG$r4TcrVsTsibcVF3ZfZIJPjLNvq73wyusLShDmpSZeKM=
 

登录验证

  Django的验证系统已经帮我们实现了登录验证的功能。通过django.contrib.auth.authenticate即可实现。这个方法只能通过usernamepassword来进行验证。示例代码如下:

def index(request):
    user = authenticate(username="jkc", password="111111")
    if user:
        return HttpResponse("登录成功")
    else:
        return HttpResponse("登录失败")

 

扩展用户模型

  Django内置的User模型虽然已经足够强大了。但是有时候还是不能满足我们的需求。比如在验证用户登录的时候,他用的是用户名作为验证,而我们有时候需要通过手机号码或者邮箱来进行验证。还有比如我们想要增加一些新的字段。那么这时候我们就需要扩展用户模型了。扩展用户模型有多种方式。这里我们来一一讨论下。
 

继承自AbstractUser

  对于authenticate不满意,并且不想要修改原来User对象上的一些字段,但是想要增加一些字段,那么这时候可以直接继承自django.contrib.auth.models.AbstractUser,其实这个类也是django.contrib.auth.models.User的父类。比如我们想要在原来User模型的基础之上添加一个phone字段。示例代码如下:

from django.contrib.auth.base_user import BaseUserManager
from django.db import models
from django.contrib.auth.models import AbstractUser


class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self,phone,password,**extra_fields):
        if not phone:
            raise ValueError("请填入手机号码!")
        if not password:
            raise ValueError('请输入密码!')
        user = self.model(phone=phone,*extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_user(self,phone,password,**extra_fields):
        extra_fields.setdefault('is_superuser',False)
        return self._create_user(phone,password)

    def create_superuser(self,phone,password,**extra_fields):
        extra_fields['is_superuser'] = True
        return self._create_user(phone,password)


class User(AbstractUser):
    phone = models.CharField(max_length=11, unique=True, verbose_name="手机号码")
    # 指定phone作为USERNAME_FIELD,以后使用authenticate
    # 函数验证的时候,就可以根据phone来验证,而不是原来的username
    USERNAME_FIELD = 'phone'
    # 提醒用户输入的字段
    REQUIRED_FIELDS = []

    # 重新定义Manager对象,在创建user的时候使用phone和password,而不是使用username和password
    objects = UserManager()

然后再在settings中配置好AUTH_USER_MODEL=yourapp.User
注意:这种方式因为破坏了原来User模型的表结构,所以必须要在第一次migrate前就先定义好。
以上我们重新定义了User模型,新增了phone字段,并把phone作为校验字段,我们先来看下数据库的表结构
Django(60)Django内置User模型源码分析及自定义User
接下里我们通过createsuperuser命令来创建超级用户
Django(60)Django内置User模型源码分析及自定义User
我们会发现创建超级用户的时候,不再需要username字段来校验了,接下来我们验证一下登录,现在的结构需要用phone字段和密码来登录,而不是使用username,我们编写视图函数来尝试

def index(request):
    # 先使用手机号密码登录
    user = authenticate(username="12345678901", password="admin123")
    if user:
        return HttpResponse('手机号密码登录成功')
    else:
        return HttpResponse('手机号密码登录失败')

然后访问视图,返回手机号密码登录成功,说明现在校验的字段的内容是手机号,我们再来试试使用用户名能否登录成功

def index(request):
    # 由于之前未设置username,这里先为id为1的用户设置username
    user = User.objects.get(pk=1)
    user.username = "jkc"
    user.save()
    print("保存成功")
    u = authenticate(username="jkc", password="admin123")
    if u:
        return HttpResponse('用户名登录成功')
    else:
        return HttpResponse('用户名登录失败')

我们访问视图,最后返回的是手机号验证码登录失败,说明现在username校验的是手机号,我们输入用户名是校验不通过的

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/165763.html原文链接:https://javaforall.net

(0)
上一篇 2022年7月31日 下午3:00
下一篇 2022年7月31日 下午3:00


相关推荐

  • padEnd_np.pad函数

    padEnd_np.pad函数ES2017引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全下面有一个小练习身份号只显示后四位

    2025年10月11日
    6
  • 文件上传文件的权限–lnmp 环境配置,尤其整个项目复制过来

    文件上传文件的权限–lnmp 环境配置,尤其整个项目复制过来

    2021年10月27日
    41
  • java与javaee_java和javaEE有什么区别?

    java与javaee_java和javaEE有什么区别?Java 是程序设计语言 由相关的 Java 语言标准定义 标准如下 Java 程序长这样 publicclassH publicstatic String args System out println Hello World JavaEE 是开发企业应用的标准规范 长这样 JavaEE 除了面向阅读者的规范形式 还有面向程

    2026年3月16日
    2
  • qmake中文官方文档

    qmake中文官方文档qmakeQMake提供了一个用于管理应用程序,库,其他组件的构建过程的面向工程系统QMake扩展了每个工程文件的信息,生成一个执行编译和连接过程的Makefile本文翻译自Qt4.8官方文档。一、QMake使用QMake提供了一个用于管理应用程序、库、其它组件的构建过程的面向工程系统。QMake扩展了每个工程文件的信息,生成一个执行编译和链接过程的必须命令的MakeFile。1、描述工程工程文件.pro描述了工程信息。工程文件信息会被qmake用于生成包含构建过程中所需的所有命令的Mak

    2022年5月19日
    47
  • 汇编指令英文全称

    汇编指令英文全称有时我们需要查看汇编代码去分析软件问题 汇编代码才能最直观地反映出软件的问题 为了方便大家理解并记忆汇编指令 此处列出常用汇编指令的英文全称 以供参考

    2026年3月26日
    1
  • JavaScript对象遍历与比较

    JavaScript对象遍历与比较JS 写多了发现对于一些基本类型的相关处理还不够熟练 这里总结下 Js 里的基本类型和相关类型的一些常见操作 基本的流程控制语句也与其他语言类似 三种控制结构 顺序 判断 循环 javaScript 基本类型包含字符串 数字 布尔 数组 对象 Null Undefined 都是弱类型 变量声明为 var 也可以不用 var 声明 这样运行并不会出错 只是变量作用的值域不同而已 关于具体不同于闭包等相关内容属于高

    2026年3月26日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号