前言

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表,如下

创建超级用户

创建超级用户有两种方式。第一种是使用代码的方式。用代码创建超级用户跟创建普通用户非常的类似,只不过是使用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作为校验字段,我们先来看下数据库的表结构



接下里我们通过createsuperuser命令来创建超级用户



我们会发现创建超级用户的时候,不再需要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校验的是手机号,我们输入用户名是校验不通过的

Django(60)Django内置User模型源码分析及自定义User的更多相关文章

  1. Django中CBV(Class Base Views)模型源码分析

    在view文件中编写一个类,并配置好路由 class Test(View): def get(self, request, *args, **kwargs): return HttpResponse( ...

  2. SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)

    Spring Boot默认使用Tomcat作为嵌入式的Servlet容器,只要引入了spring-boot-start-web依赖,则默认是用Tomcat作为Servlet容器: <depend ...

  3. Matlab.NET混合编程技巧之——直接调用Matlab内置函数(附源码)

    原文:[原创]Matlab.NET混合编程技巧之--直接调用Matlab内置函数(附源码) 在我的上一篇文章[原创]Matlab.NET混编技巧之——找出Matlab内置函数中,已经大概的介绍了mat ...

  4. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...

  5. Django 之 restframework 版本控制的使用以及源码分析

    Django rest_framework 之 版本控制 一.何为版本控制: ​ 用于版本的控制 二.内置的版本控制类: from rest_framework.versioning import Q ...

  6. Django(64)频率认证源码分析与自定义频率认证

    前言 有时候我们发送手机验证码,会发现1分钟只能发送1次,这是做了频率限制,限制的时间次数,都由开发者自己决定 频率认证源码分析 def check_throttles(self, request): ...

  7. Django框架(十八)—— CBV源码分析、restful规范、restframework框架

    目录 CBV源码分析.restful规范.restframework框架 一.CBV源码分析 1.url层的使用CBV 2.as_view方法 3.view方法 4.dispatch方法(可以在视图层 ...

  8. Django(50)drf异常模块源码分析

    异常模块源码入口 APIView类中dispatch方法中的:response = self.handle_exception(exc) 源码分析 我们点击handle_exception跳转,查看该 ...

  9. Django(63)drf权限源码分析与自定义权限

    前言 上一篇我们分析了认证的源码,一个请求认证通过以后,第二步就是查看权限了,drf默认是允许所有用户访问 权限源码分析 源码入口:APIView.py文件下的initial方法下的check_per ...

随机推荐

  1. Linux中grep工具的使用

    Grep grep(Globel Search Regular Expression and Printing out the line)全面搜索正则表达式并把行打印出来,是一种强大的文本搜索工具,是 ...

  2. John the Ripper破解密码

    目录 John the Ripper 破解Linux系统密码 破解Windows系统密码 John the Ripper John the Ripper是一个快速的密码破解工具,用于在已知密文的情况下 ...

  3. 『政善治』Postman工具 — 4、HTTP请求基础组成部分介绍

    目录 1.Method 2.URL 3.Headers 4.body 一般来说,所有的HTTP Request都有最基础的4个部分组成:URL. Method. Headers和body. 1.Met ...

  4. maven打war包

    测试/本地 mvn clean package -Dmaven.test.skip=true 生产服务器打包命令 mvn clean package -P prod -Dmaven.test.skip ...

  5. java面试一日一题:如何判断一个对象是否为垃圾对象

    问题:请讲下在java中如何判断一个对象是否为垃圾 分析:该问题主要考察对java中的垃圾回收,用什么方式去识别一个对象是垃圾: 回答要点: 主要从以下几点去考虑, 1.GC回收的是什么,回收发生在内 ...

  6. 基于python对B站收藏夹按照视频发布时间进行排序

    基于python对B站收藏夹按照视频发布时间进行排序 前言 在最一开始,我的B站收藏一直是存放在默认收藏夹中,但是随着视频收藏的越来越多,没有分类的视频放在一起,想在众多视频中找到想要的视频非常困难, ...

  7. C++逆向分析----多重继承和菱形继承

    多重继承 多重继承是指C++类同时继承两个类或两个以上的类. class Test { public: int num1; Test() { num1 = 1; } virtual void Proc ...

  8. ES6新增数组的一些思考和使用

    ES6数组的新增 伪数组转换为数组的两种方法 Array.from()把一个伪数组转换为一个真正的数组 伪数组:有下标和length,但是不能使用数组方法 let lis = document.que ...

  9. [Qt] 事件机制(四)

    滚轮事件:滚动滚轮实现窗口大小缩放 widget.h中增加: protected: void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; void ...

  10. commit信息修改

    场景:向社区提交commit信息,code reviewer给你回复说,请添加TrivialFix并且完善commit信息.好吧,虽然这对代码的运行无关紧要,但是对于日后的代码管理是很有必要的. 解决 ...