Django知识点 - user的自定制

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

发布了64 篇原创文章 · 获赞 46 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_42737442/article/details/90779232
今日推荐