对django的User模型和四种扩展/重写方法小结


Posted in Python onAugust 17, 2019

User模型

User模型是这个框架的核心部分。他的完整的路径是在django.contrib.auth.models.User。以下对这个User对象做一个简单了解:

字段:

内置的User模型拥有以下的字段:

username: 用户名。150个字符以内。可以包含数字和英文字符,以及_、@、+、.和-字符。不能为空,且必须唯一!

first_name:歪果仁的first_name,在30个字符以内。可以为空。

last_name:歪果仁的last_name,在150个字符以内。可以为空。

email:邮箱。可以为空。

password:密码。经过哈希过后的密码。

groups:分组。一个用户可以属于多个分组,一个分组可以拥有多个用户。groups这个字段是跟Group的一个多对多的关系。

user_permissions:权限。一个用户可以拥有多个权限,一个权限可以被多个用户所有用。和Permission属于一种多对多的关系。

is_staff:是否可以进入到admin的站点。代表是否是员工。

is_active:是否是可用的。对于一些想要删除账号的数据,我们设置这个值为False就可以了,而不是真正的从数据库中删除。

is_superuser:是否是超级管理员。如果是超级管理员,那么拥有整个网站的所有权限。

last_login:上次登录的时间。

date_joined:账号创建的时间。

登录验证:

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

from django.contrib.auth import authenticate
user = authenticate(username='Tom', password='111111')
# 如果验证通过了,那么就会返回一个user对象。
if user is not None:
  # 执行验证通过后的代码
else:
  # 执行验证没有通过的代码。

扩展用户模型:

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

1. 设置Proxy模型:

作用: 给模型增加操作方法

局限: 不能增加或减少User模型的字段

好处: 不破坏原来的User模型的表结构

如果你对Django提供的字段,以及验证的方法都比较满意,没有什么需要改的。但是只是需要在他原有的基础之上增加一些操作的方法。那么建议使用这种方式。示例代码如下:

# 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)

在以上,我们定义了一个Person类,让他继承自User,并且在Meta中设置proxy=True,说明这个只是User的一个代理模型。他并不会影响原来User模型在数据库中表的结构。以后如果你想方便的获取所有黑名单的人,那么你就可以通过Person.get_blacklist()就可以获取到。并且User.objects.all()和Person.objects.all()其实是等价的。因为他们都是从User这个模型中获取所有的数据。

2. 一对一外键:

作用: 给模型增加新的字段, 新方法

局限: 只能增加, 不能减少字段, 不能修改户验证方法: authenticate

好处: 不破坏原来的User模型的表结构

如果你对用户验证方法authenticate没有其他要求,就是使用username和password即可完成。但是想要在原来模型的基础之上添加新的字段,那么可以使用一对一外键的方式。示例代码如下:

# models.py
from django.contrib.auth.models import User
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save
 
class UserExtension(models.Model):
  user = models.OneToOneField(User,on_delete=models.CASCADE,related_name='extension')
  birthday = models.DateField(null=True,blank=True)
  school = models.CharField(max_length=100)
 
 
@receiver(post_save,sender=User)
def create_user_extension(sender,instance,created,**kwargs):
  if created:
    UserExtension.objects.create(user=instance)
  else:
    instance.extension.save()

以上定义一个UserExtension的模型,并且让她和User模型进行一对一的绑定,以后我们新增的字段,就添加到UserExtension上。并且还写了一个接受保存模型的信号处理方法,只要是User调用了save方法,那么就会创建一个UserExtension和User进行绑定。

# views.py
from django.contrib.auth.models import User
from django.http import HttpResponse
 
 
def one_to_one_view(request):
  user = User.objects.create_user(username='Tom',email='tom@qq.com',password='111111')
  # 给扩展的字段设置值
  user.extension.school = 'Harvard'
  user.save()
  return HttpResponse('一对一扩展User模型')

3. 继承自AbstractUser:

作用: 给模型增加新的字段, 修改户验证方法: authenticate

局限: 只能增加, 不能减少字段

坏处: 破坏了原来的User模型的表结构

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

# 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)

然后再在settings中配置好

# settings.py
AUTH_USER_MODEL = 'youappname.User'

这种方式因为破坏了原来User模型的表结构,所以必须要在第一次migrate前就先定义好。

4. 继承自AbstractBaseUser模型:

作用: 给模型增加或减少字段, 修改户验证方法: authenticate

坏处: 破坏了原来的User模型的表结构

注意: 继承自AbstractBaseUser同时还要继承PermissionsMixin

如果你想修改默认的验证方式,并且对于原来User模型上的一些字段不想要,那么可以自定义一个模型,然后继承自AbstractBaseUser,再添加你想要的字段。这种方式会比较麻烦,最好是确定自己对Django比较了解才推荐使用。步骤如下:

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

# 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

其中password和last_login是在AbstractBaseUser中已经添加好了的,我们直接继承就可以了。然后我们再添加我们想要的字段。比如email、username、telephone等。这样就可以实现自己想要的字段了。但是因为我们重写了User,所以应该尽可能的模拟User模型:

USERNAME_FIELD:用来描述User模型名字字段的字符串,作为唯一的标识。如果没有修改,那么会使用USERNAME来作为唯一字段。

REQUIRED_FIELDS:一个字段名列表,用于当通过createsuperuser管理命令创建一个用户时的提示。

is_active:一个布尔值,用于标识用户当前是否可用。

get_full_name():获取完整的名字。

get_short_name():一个比较简短的用户名。

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

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

# settings.py AUTH_USER_MODEL = 'youappname.User'

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

这种方式因为破坏了原来User模型的表结构,所以必须要在第一次migrate前就先定义好。

以上这篇对django的User模型和四种扩展/重写方法小结就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python多线程编程(七):使用Condition实现复杂同步
Apr 05 Python
Python实现提取文章摘要的方法
Apr 21 Python
python调用java模块SmartXLS和jpype修改excel文件的方法
Apr 28 Python
Python连接MySQL并使用fetchall()方法过滤特殊字符
Mar 13 Python
Python实现文件复制删除
Apr 19 Python
Python实现基于PIL和tesseract的验证码识别功能示例
Jul 11 Python
python批量修改图片后缀的方法(png到jpg)
Oct 25 Python
python实现在遍历列表时,直接对dict元素增加字段的方法
Jan 15 Python
使用python的pandas为你的股票绘制趋势图
Jun 26 Python
Python 脚本实现淘宝准点秒杀功能
Nov 13 Python
python3.6使用SMTP协议发送邮件
May 20 Python
Django如何实现防止XSS攻击
Oct 13 Python
python3.6编写的单元测试示例
Aug 17 #Python
python3 实现的对象与json相互转换操作示例
Aug 17 #Python
python3实现的zip格式压缩文件夹操作示例
Aug 17 #Python
django 通过url实现简单的权限控制的例子
Aug 16 #Python
对Django中内置的User模型实例详解
Aug 16 #Python
对Django中的权限和分组管理实例讲解
Aug 16 #Python
django创建最简单HTML页面跳转方法
Aug 16 #Python
You might like
PHP中使用file_get_contents post数据代码例子
2015/02/13 PHP
php中mysql操作buffer用法详解
2015/03/19 PHP
php+redis在实际项目中HTTP 500: Internal Server Error故障排除
2017/02/05 PHP
php使用scandir()函数扫描指定目录下所有文件示例
2019/06/08 PHP
JS中style属性
2006/10/11 Javascript
Webkit的跨域安全问题说明
2011/09/13 Javascript
什么是DOM(Document Object Model)文档对象模型
2012/03/05 Javascript
JavaScript输入邮箱自动提示实例代码
2014/01/13 Javascript
jQuery实现自动调整字体大小的方法
2015/06/15 Javascript
jQuery实现的简单折叠菜单(折叠面板)效果代码
2015/09/16 Javascript
Bootstrap编写一个在当前网页弹出可关闭的对话框 非弹窗
2016/06/30 Javascript
JavaScript组成、引入、输出、运算符基础知识讲解
2016/12/08 Javascript
基于Vue制作组织架构树组件
2017/12/06 Javascript
CSS3结合jQuery实现动画效果及回调函数的实例
2017/12/27 jQuery
解决vue-router进行build无法正常显示路由页面的问题
2018/03/06 Javascript
使用layui前端框架弹出form表单以及提交的示例
2019/10/25 Javascript
如何在postman测试用例中实现断言过程解析
2020/07/09 Javascript
vue实现div可拖动位置也可改变盒子大小的原理
2020/09/16 Javascript
javascript this指向相关问题及改变方法
2020/11/19 Javascript
python 实现删除文件或文件夹实例详解
2016/12/04 Python
磁盘垃圾文件清理器python代码实现
2020/08/24 Python
Python for循环与range函数的使用详解
2019/03/23 Python
python3.x实现base64加密和解密
2019/03/28 Python
python实现车牌识别的示例代码
2019/08/05 Python
基于Python的图像数据增强Data Augmentation解析
2019/08/13 Python
python区分不同数据类型的方法
2019/10/14 Python
Python调用ffmpeg开源视频处理库,批量处理视频
2020/11/16 Python
移动端Web页面的CSS3 flex布局快速上手指南
2016/05/31 HTML / CSS
详解canvas绘制多张图的排列顺序问题
2019/01/21 HTML / CSS
英国水族馆和池塘用品购物网站:Warehouse Aquatics
2019/08/29 全球购物
竞聘演讲稿范文
2014/01/12 职场文书
房产协议书范本
2014/10/18 职场文书
民间借贷借条如何写
2015/05/26 职场文书
团委副书记工作总结
2015/08/14 职场文书
初一英语教学反思
2016/02/15 职场文书
SpringBoot 集成短信和邮件 以阿里云短信服务为例
2022/04/22 Java/Android