Django2.0-验证和授权(2)-User模型拓展

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/abc666666_6/article/details/84202520

扩展用户模型

代理模型

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

如果只是需要在默认的基础之上增加一些操作的方法。则使用代理模型的方法

# models.py
from django.db import models
from django.contrib.auth.models import User

# 如果模型是一个代码模型,那么就不能在这里模型中添加新的Field!!!!!!!!!!!
class ProxyUser(User):
    class Meta:
        proxy = True

    @classmethod
    def get_blacklist(cls):
        # seek that is_active = False
        return cls.objects.filter(is_active=False)

定义了一个ProxyUser类继承自User,并且在Meta中设置proxy=True,说明这个只是User的一个代理模型。并不会影响原来User模型在数据库中表的结构。

以后如果想方便的获取所有黑名单的人,那么就可以通过ProxyUser.get_blacklist()就可以获取到。

并且User.objects.all()Person.objects.all()其实是等价的。因为它们都是从User这个模型中获取所有的数据。

from django.http import HttpResponse
from .models import ProxyUser


def proxy(reqeust):
    blacklist = ProxyUser.get_blacklist()  # 代理模型的类方法(查找is_active=False的对象)
    for user in blacklist:
        print(user.username)
    # 再次注意ProxyUser.object.all() == User.object.all()
    return HttpResponse("proxy")

2. 一对一外键

如果你对用户验证方法authenticate没有其他要求,就是使用usernamepassword即可完成。
但是想要在原来模型的基础之上添加新的字段

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 ExtraUser(models.Model):  # 一对一方式扩展
    all_user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="extra")  # 注意这里更改User中自添加的extrauser字段改名为extra
    phone = models.CharField(max_length=11)  # 新添加的字段
    address = models.CharField(max_length=100)  # 新添加的字段


# 将User和一对一扩展模型进行绑定
# 原型是: receiver(signal, **kwargs)
@receiver(post_save, sender=User)  # 当User产生post_save信号时 
def handler_user_extra_content(sender, instance, created, **kwargs):
    if created:  # 如果第一次创建
        ExtraUser.objects.create(all_user=instance)  # 绑定User实例到ExtraUser的all_user字段
    else:
        instance.extra.save()  # 保存ExtraUser的内容 ,注意extra是ExtraUser的all_user字段外键的related_name名

定义一个ExtraUser的模型,并且让它和User模型进行一对一的绑定,以后新增的字段,就添加到ExtraUser上。

还写了一个接受保存模型的信号处理方法,只要是User调用了save方法,那么就会创建一个ExtraUserUser进行绑定。

# views.py 使用该模型
fron django.http import HttpResponse

def one_to_one_authenticate(phone, password):  # 自定义的authenticate
    user = User.objects.filter(extra__phone=phone).first()  
    # User由于被引用,其下默认生成一个字段,且被改名为extra
    if user:
        is_correct = user.check_password(password)
        if is_correct:
            return user
        else:
            return None
    else:
        return None


def one_to_one(request):
    user = User.objects.create_user(username="lee", email='[email protected]', password=111111)
    user = User.objects.get(username="lee")
    user.extra.phone = 10086100861  # 更改ExtraUser里的字段信息
    user.extra.address = "china"
    user.save()
    
    # 假设下面是用户输入
    phone = 10086100861
    password = 111111
    user = one_to_one_authenticate(phone=phone, password=password)  # 使用自己的authenticate
    if user:
        print(user.username)
    else:
        print("no such user")
    return HttpResponse("one to one successful")

3. 继承自AbstractUser

authenticate不满意,并且不想要修改原来User对象上的一些字段,且想要增加一些字段,那么这时候可以直接继承自django.contrib.auth.models.AbstractUser,这个类也是django.contrib.auth.models.User的父类。

比如想要在原来User模型的基础之上添加一个phoneaddress字段。

from django.contrib.auth.models import AbstractUser, BaseUserManager  
# 前者是User的父类

class UserManager(BaseUserManager):  
    def _create_user(self, phone, username, password, **kwargs):
        # 这是一个受保护函数,只能被类自己中调用
        # 作为create_user和create_superuser的被调用函数
        if not phone:
            raise ValueError("必须传递手机号码")
        if not password:
            raise ValueError("必须传递密码")
        user = self.model(phone=phone, username=username, **kwargs)  # self.model表示当前模型
        user.set_password(password)  # password只能这样设置
        user.save()
        return user

    def create_user(self, phone, username, password, **kwargs):
        kwargs["is_superuser"] = False  # 添加is_superuser键值对
        return self._create_user(phone=phone, username=username, password=password, **kwargs)

    def create_superuser(self, phone, username, password, **kwargs):
        kwargs["is_superuser"] = True
        return self._create_user(phone=phone, username=username, password=password, **kwargs)


class InheritOne(AbstractUser):  # 自定义的User类
    phone = models.CharField(max_length=11, unique=True)
    address = models.CharField(max_length=100)

    # 指定phone作为USERNAME_FIFLE,使用authenticate函数验证的时候,就可以用phone的值来验证而不是username
    USERNAME_FIELD = 'phone'  # 到时候用的时候是username = phone's value
    REQUIRED_FIELDS = []  # 命令行创建超级用户的时候系统提示要添加的内容

    # 重新指定Manager对象,为了在使用object.create_user和object.create_superuser的时候
    # 使用phone和password而不是username和password
    objects = UserManager()

然后再在settings中配置好AUTH_USER_MODEL=app_name.InheritOne

扫描二维码关注公众号,回复: 4530506 查看本文章

这种方式因为破坏了原来User模型的表结构,所以必须要在第一次migrate前就先定义好(如果已经migrate,练习时需要删除所有表和app的migrations包内的迁移文件)。

# 使用
from django.http import HttpResponse
from .models import InheritOne


def inherit_one(request):
    InheritOne.objects.create_user(phone=100861008611, username="jack", password=111111)
    InheritOne.objects.create_superuser(phone=13100001111, username='superJack', password=222222)
    user = authenticate(request, username=100861008611, password=111111)  # 这里的username是phone's value, 在InheritOne中的USERNAME_FIELD定义的字段代表的值,即'phone'字段的值
    if user:
        print("存在")
    else:
        print("不存在")
    return HttpResponse("inherit from AbstractUser")

4. 继承自AbstractBaseUser模型

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

  1. 创建模型。

    from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
    from django.contrib.auth.models import PermissionsMixin
    
    
    class InheritTwo(AbstractBaseUser, PermissionsMixin):
        phone = models.CharField(max_length=11, unique=True)
        username = models.CharField(max_length=20)
        password = models.CharField(max_length=20)
        address = models.CharField(max_length=100)
        is_active = models.BooleanField(default=True)  # 这个要加
    
        USERNAME_FIELD = 'phone'  # authenticate函数的usernma参数指定为phone字段的值
        REQUIRED_FIELDS = []
    
        objects = UserManager() 
    
        def get_full_name(self):  # 可以参考AbstractUser的函数
            return self.username
    
        def get_short_name(self):
            return self.username
    

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

    • USERNAME_FIELD:用来描述User模型名字字段的字符串,作为唯一的标识。如果没有修改,那么会使用USERNAME来作为唯一字段。
    • REQUIRED_FIELDS:一个字段名列表,用于当通过createsuperuser管理命令创建一个用户时的提示。
    • is_active:一个布尔值,用于标识用户当前是否可用。
    • get_full_name():获取完整的名字。
    • get_short_name():一个比较简短的用户名。
  2. 重新定义UserManager:因为默认的UserManager在创建用户的时候使用的是usernamepassword,这里需要增加一个phone参数。

    class UserManager(BaseUserManager):
        def _create_user(self, phone, username, password, **kwargs):
            # 这是一个受保护函数,只能被类自己中调用
            # 作为create_user和create_superuser的被调用函数
            if not phone:
                raise ValueError("必须传递手机号码")
            if not password:
                raise ValueError("必须传递密码")
            user = self.model(phone=phone, username=username, **kwargs)  # self.model表示当前模型
            user.set_password(password)  # password只能这样设置
            user.save()
            return user
    
        def create_user(self, phone, username, password, **kwargs):
            kwargs["is_superuser"] = False  # 添加is_superuser键值对
            return self._create_user(phone=phone, username=username, password=password, **kwargs)
    
        def create_superuser(self, phone, username, password, **kwargs):
            kwargs["is_superuser"] = True
            return self._create_user(phone=phone, username=username, password=password, **kwargs)
    
  3. 在创建了新的User模型后,还需要在settings.py中配置好。配置AUTH_USER_MODEL='appname.InheritTwo'

  4. 使用1

    from django.http import HttpResponse
    from .models import InheritTwo
    
    
    def inherit_two(request):
       InheritTwo.objects.create_user(phone=10086100861, username='jack', password=11111)
       InheritTwo.objects.create_superuser(phone=10086100862, username='lee', password=222222)
       user = authenticate(request, username=10086100862, password=222222)
       if user:
           print('存在')
       else:
           print('不存在')
       return HttpResponse("inherit two")
    

    1

  5. 使用2
    比如以后有一个Article模型,需要通过外键引用这个User模型,那么可以通过以下两种方式引用。
    第一种就是直接将User导入到当前文件中。

     from django.db import models
     from otherapp.models import InheritTwo
     class Article(models.Model):
         title = models.CharField(max_length=100)
         content = models.TextField()
         author = models.ForeignKey(User, on_delete=models.CASCADE)
    

    为了更好的使用性,建议还是将User抽象出来,使用settings.py中的AUTH_USER_MODEL来表示。可以使用get_user_model()函数来读取该值

     from django.db import models
     from django.conf import settings
     from django.contrib.auth import get_user_model
     
     
     class Article(models.Model):
         title = models.CharField(max_length=100)
         content = models.TextField()
         author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    

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

猜你喜欢

转载自blog.csdn.net/abc666666_6/article/details/84202520