代理模型

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

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

  1. # models.py
  2. from django.db import models
  3. from django.contrib.auth.models import User
  4. # 如果模型是一个代码模型,那么就不能在这里模型中添加新的Field!!!!!!!!!!!
  5. class ProxyUser(User):
  6. class Meta:
  7. proxy = True
  8. @classmethod
  9. def get_blacklist(cls):
  10. # seek that is_active = False
  11. return cls.objects.filter(is_active=False)

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

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

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

  1. from django.http import HttpResponse
  2. from .models import ProxyUser
  3. def proxy(reqeust):
  4. blacklist = ProxyUser.get_blacklist() # 代理模型的类方法(查找is_active=False的对象)
  5. for user in blacklist:
  6. print(user.username)
  7. # 再次注意ProxyUser.object.all() == User.object.all()
  8. return HttpResponse("proxy")

2. 一对一外键

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

  1. from django.contrib.auth.models import User
  2. from django.db import models
  3. from django.dispatch import receiver
  4. from django.db.models.signals import post_save
  5. class ExtraUser(models.Model): # 一对一方式扩展
  6. all_user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="extra") # 注意这里更改User中自添加的extrauser字段改名为extra
  7. phone = models.CharField(max_length=11) # 新添加的字段
  8. address = models.CharField(max_length=100) # 新添加的字段
  9. # 将User和一对一扩展模型进行绑定
  10. # 原型是: receiver(signal, **kwargs)
  11. @receiver(post_save, sender=User) # 当User产生post_save信号时
  12. def handler_user_extra_content(sender, instance, created, **kwargs):
  13. if created: # 如果第一次创建
  14. ExtraUser.objects.create(all_user=instance) # 绑定User实例到ExtraUser的all_user字段
  15. else:
  16. instance.extra.save() # 保存ExtraUser的内容 ,注意extra是ExtraUser的all_user字段外键的related_name名

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

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

  1. # views.py 使用该模型
  2. fron django.http import HttpResponse
  3. def one_to_one_authenticate(phone, password): # 自定义的authenticate
  4. user = User.objects.filter(extra__phone=phone).first()
  5. # User由于被引用,其下默认生成一个字段,且被改名为extra
  6. if user:
  7. is_correct = user.check_password(password)
  8. if is_correct:
  9. return user
  10. else:
  11. return None
  12. else:
  13. return None
  14. def one_to_one(request):
  15. user = User.objects.create_user(username="lee", email='lee@163.com', password=111111)
  16. user = User.objects.get(username="lee")
  17. user.extra.phone = 10086100861 # 更改ExtraUser里的字段信息
  18. user.extra.address = "china"
  19. user.save()
  20. # 假设下面是用户输入
  21. phone = 10086100861
  22. password = 111111
  23. user = one_to_one_authenticate(phone=phone, password=password) # 使用自己的authenticate
  24. if user:
  25. print(user.username)
  26. else:
  27. print("no such user")
  28. return HttpResponse("one to one successful")

3. 继承自AbstractUser

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

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

  1. from django.contrib.auth.models import AbstractUser, BaseUserManager
  2. # 前者是User的父类
  3. class UserManager(BaseUserManager):
  4. def _create_user(self, phone, username, password, **kwargs):
  5. # 这是一个受保护函数,只能被类自己中调用
  6. # 作为create_user和create_superuser的被调用函数
  7. if not phone:
  8. raise ValueError("必须传递手机号码")
  9. if not password:
  10. raise ValueError("必须传递密码")
  11. user = self.model(phone=phone, username=username, **kwargs) # self.model表示当前模型
  12. user.set_password(password) # password只能这样设置
  13. user.save()
  14. return user
  15. def create_user(self, phone, username, password, **kwargs):
  16. kwargs["is_superuser"] = False # 添加is_superuser键值对
  17. return self._create_user(phone=phone, username=username, password=password, **kwargs)
  18. def create_superuser(self, phone, username, password, **kwargs):
  19. kwargs["is_superuser"] = True
  20. return self._create_user(phone=phone, username=username, password=password, **kwargs)
  21. class InheritOne(AbstractUser): # 自定义的User类
  22. phone = models.CharField(max_length=11, unique=True)
  23. address = models.CharField(max_length=100)
  24. # 指定phone作为USERNAME_FIFLE,使用authenticate函数验证的时候,就可以用phone的值来验证而不是username
  25. USERNAME_FIELD = 'phone' # 到时候用的时候是username = phone's value
  26. REQUIRED_FIELDS = [] # 命令行创建超级用户的时候系统提示要添加的内容
  27. # 重新指定Manager对象,为了在使用object.create_user和object.create_superuser的时候
  28. # 使用phone和password而不是username和password
  29. objects = UserManager()

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

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

  1. # 使用
  2. from django.http import HttpResponse
  3. from .models import InheritOne
  4. def inherit_one(request):
  5. InheritOne.objects.create_user(phone=100861008611, username="jack", password=111111)
  6. InheritOne.objects.create_superuser(phone=13100001111, username='superJack', password=222222)
  7. user = authenticate(request, username=100861008611, password=111111) # 这里的username是phone's value, 在InheritOne中的USERNAME_FIELD定义的字段代表的值,即'phone'字段的值
  8. if user:
  9. print("存在")
  10. else:
  11. print("不存在")
  12. return HttpResponse("inherit from AbstractUser")

4. 继承自AbstractBaseUser模型

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

  1. 创建模型。 ```python 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) # 这个要加

  1. USERNAME_FIELD = 'phone' # authenticate函数的usernma参数指定为phone字段的值
  2. REQUIRED_FIELDS = []
  3. objects = UserManager()
  4. def get_full_name(self): # 可以参考AbstractUser的函数
  5. return self.username
  6. def get_short_name(self):
  7. return self.username
  1. 1. <br />其中`password``last_login`是在`AbstractBaseUser`中已经添加好了的,直接继承就可以了。<br />
  2. 然后再添加想要的字段。比如`username``phone`等。这样就可以实现自己想要的字段了。但是因为重写了`User`,所以应该尽可能的模拟`User`模型:
  3. - `USERNAME_FIELD`:用来描述`User`模型名字字段的字符串,作为唯一的标识。如果没有修改,那么会使用`USERNAME`来作为唯一字段。
  4. - `REQUIRED_FIELDS`:一个字段名列表,用于当通过`createsuperuser`管理命令创建一个用户时的提示。
  5. - `is_active`:一个布尔值,用于标识用户当前是否可用。
  6. - `get_full_name()`:获取完整的名字。
  7. - `get_short_name()`:一个比较简短的用户名。
  8. 2. 重新定义`UserManager`:因为默认的`UserManager`在创建用户的时候使用的是`username``password`,这里需要增加一个`phone`参数。
  9. ```shell
  10. class UserManager(BaseUserManager):
  11. def _create_user(self, phone, username, password, **kwargs):
  12. # 这是一个受保护函数,只能被类自己中调用
  13. # 作为create_user和create_superuser的被调用函数
  14. if not phone:
  15. raise ValueError("必须传递手机号码")
  16. if not password:
  17. raise ValueError("必须传递密码")
  18. user = self.model(phone=phone, username=username, **kwargs) # self.model表示当前模型
  19. user.set_password(password) # password只能这样设置
  20. user.save()
  21. return user
  22. def create_user(self, phone, username, password, **kwargs):
  23. kwargs["is_superuser"] = False # 添加is_superuser键值对
  24. return self._create_user(phone=phone, username=username, password=password, **kwargs)
  25. def create_superuser(self, phone, username, password, **kwargs):
  26. kwargs["is_superuser"] = True
  27. return self._create_user(phone=phone, username=username, password=password, **kwargs)
  1. 在创建了新的User模型后,还需要在settings.py中配置好。配置AUTH_USER_MODEL='appname.InheritTwo'
  2. 使用1 ```python 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. 4. <br />![](https://cdn.nlark.com/yuque/0/2019/png/367873/1559645165747-ccd36e7d-a810-4502-9b64-53fb4feae2a3.png#align=left&display=inline&height=280&originHeight=280&originWidth=1005&size=0&status=done&width=1005)
  2. 5. 使用2<br />
  3. 比如以后有一个`Article`模型,需要通过外键引用这个`User`模型,那么可以通过以下两种方式引用。<br />
  4. 第一种就是直接将`User`导入到当前文件中。
  5. ```python
  6. from django.db import models
  7. from otherapp.models import InheritTwo
  8. class Article(models.Model):
  9. title = models.CharField(max_length=100)
  10. content = models.TextField()
  11. author = models.ForeignKey(User, on_delete=models.CASCADE)

  1. 为了更好的使用性,建议还是将User抽象出来,使用settings.py中的AUTH_USER_MODEL来表示。可以使用get_user_model()函数来读取该值 ```python 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前就先定义好。