DjangoLearning_charter11验证模型User及其拓展方式
验证和授权系统
验证和授权系统简介
Django有一个内置的授权系统, 他用来处理用户、分组、权限以及基于cookie的会话系统。 Django的授权系统包括验证和授权两个部分。 验证是验证这个用户是否是他声称的人(比如用户名和密码验证, 角色验证), 授权是给与他相应的权限。
1、包括以下几个方面:
用户
权限
分组
一个可以配置的密码哈希系统
一个可插拔(可用可删, 删除不会对项目产生非常大的影响)的后台管理系统
2、使用授权系统:
-
INSTALLED_APPS:
- Django.contrib.auth: 包含一个核心授权框架, 以及大部分的模型定义
- Django.contrib.contenttypes: content type 系统, 可以用来关联模型和权限
-
中间件:
- SessionMiddleware: 用来管理session
- AuthticationMiddleware: 用来处理和当前session相关联的用户
内置User模型使用
User模型是这框架的核心部分。 他的完整路径是django.contrib.auth.models.User
1、 字段解释:
- username:用户名, 150个字符以内, 可以包含_ @ + . – 等字符。 不能为空, 且必须唯一
- first_name:外国人的first_name, 在30个字符以内, 可以为空
- last_name: 外国人的last_name, 在150个字符以内, 可以为空
- email:邮箱,可以为空
- password:密码,经过哈希的密码
- groups:分组, 一个用户可以属于多个分组, 一个分组可以有多个用户。属于多对多关系
- user_permissions:权限, 也是多对多关系
- is_staff:是否可以进入到admin站点, 代表是否是员工
- is_active:是否是可用的, 对于删除账号的数据, 并不是删除数据, 而是将这个值设置为False
- is_superuser:是否是终极管理员,拥有网站的所有权限,且不受权限函数等的限制。
- last_login:上次登录时间
- first_login:账号创建时间
2、 User模型的基本用法
1. 创建用户:
通过create_user进行创建,必须传入username,email,password
from django.contrib.auth.models import User def index(request): user = User.objects.create_user('piggy', 'piggy@qq.com', '123456') # 此时user已经保存到数据库, 也可以另外做其他操作 user.last_name = 'shi' user.save() return HttpResponse('success')
创建完成的用户存储在auth_user表之中, 对密码做了加密处理
2. 创建超级用户:
使用User创建
User.objects.create_superuser(username='piggy_super', email='super@qq.com', password='123456')

使用命令行的方式创建
python manage.py createsuperuser

3. 修改密码:
因为密码是加密之后存储的, 因此修改密码不能直接用user.password = “”·
user = User.objects.get(pk=1) user.set_password('333333') user.save()
4. 登录验证:
user = authenticate(request, username='piggy', password='123456') if user: return HttpResponse('登陆成功') else: return HttpResponse('Y用户名或密码错误')
5.User模型的缺点
- 没有手机号码字段
- 验证的时候是用用户名和密码, 但是在我国通常使用邮箱或者手机号码
拓展User模型
1、Proxy模型:
可插拔、无污染的代理模型,不会对user模型产生任何的模型, 但是不能扩展模型
模型:
from django.contrib.auth.models import User class Person(User): class Meta: proxy = True @ classmethod def get_blacklist(cls): return cls.objects.filter(is_active=False)
视图函数:
def proxy_view(request): for i in Person.get_blacklist(): print(i.username, type(i.username)) print(i, type(i)) return HttpResponse('success') >> piggy <class 'str'> >> piggy <class 'front.models.Person'>
注意点: User.objects.all() Person.objects.all()完全等价
缺点:代理模型是不能用models添加字段的, 新的 fields。 但是可以添加新的属性
2、一对一外键:
如果你对用户验证方法authenticate没有其他要求, 就是使用username和password完成, 但是想要在原来的模型之上增加新字段, 那么可以用一对一外键的形式
- 用外键的方式创建模型:
class UserExtension(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='extension') telephone = models.CharField(max_length=11) school = models.CharField(max_length=100)
- 在models内接收信号:
from django.dispatch import receiver from django.db.models.signals import post_save @ receiver(post_save, sender=User) def handler_user_extension(sender, instance, created, **kwargs): if created: UserExtension.objects.create(user=instance) else: instance.extension.save()
- 视图函数(还是在User内创建用户, 必须用create_user, 否则密码不加密):
def one_view(request): User.objects.create_user(username='piggy_shi', password='132465', email='shi@qq.com') return HttpResponse('一对一扩展User模型')
- 此时会在一对一外键表内生成一条对应数据

那么, 如果想用手机号去验证, 如何操作呢?
修改验证方式:
- 首先是需要写新的验证函数
注意点: 手机号码是通过UserExtension模型通过外键关联到user上的, 因此不能直接访问telephone,而需要使用模型定义时的related_name + 双下划线+ 字段名 的形式, 即 extension__telephone
def my_authenticate(telephone, password): user = User.objects.filter(extension__telephone=telephone).first() if user: if user.check_password(password): return user else: return None else: return None
- 其次需要往数据库里存入一条有手机号的用户信息
注意点:给user指定手机号时, 也需要通过extension调用,而不能直接user.telephone进行指定,原理与上面一致
user = User.objects.create_user(username='piggy_shi4', password='111111', email='shi@qq.com') user.extension.telephone = '11111111111' user.save()
- 最后进行验证测试
telephone = request.GET.get('telephone') password = request.GET.get('password') user = my_authenticate(telephone, password) if user: print('验证成功') else: print('验证失败')
- 实现手机号码和密码验证登录

继承AbstractUser
创建模型:
- 命名为User
- 继承django默认User的父类AbstractUser(from django.contrib.auth.models import AbstractUser)
- USERNAME_FIELD 代表进行验证字段(原始User默认是username字段)
- 如果想实现通过手机号验证,必须重写验证方法, 然后在User中 传入参数 objects = 类(注意: 必须加括号, 否则只是传入类名, 而没有实例化,示例:objects = UserManager())
class User(AbstractUser):
telephone = models.CharField(max_length=11, unique=True)
school = models.CharField(max_length=100)
USERNAME_FIELD = 'telephone'
objects = UserManager()
安装APP、创建模型与项目连接
- 必须在settings中创建AUTH_USER_MODEL变量,否则会报如下错
- APP后不需要跟models, 直接跟模型名即可
- AUTH_USER_MODEL代表的字段必须为唯一, 即传入unique=True

AUTH_USER_MODEL = 'front.User'
重写objects方法
- objects是复制了BaseUserManager 的所有属性和方法
- 方法前加一个下划线表示只能在类内访问
- self.model代表当前模型, 即User
- create方法必须返回值, 否则会在视图函数报错
- user的set_password是方法, 因此是传入参数而不是指定值为password, 否则用密码值代替原始方法,且用户在数据库的密码为空
class UserManager(BaseUserManager):
def _create_user(self, telephone, password, username=None, **kwargs):
if not telephone or not password:
raise ValueError('The given telephone or password must be set')
user = self.model(telephone=telephone, username=username, **kwargs)
user.set_password(password)
# user.set_password = password # 错误的使用方式
user.save()
return user
def create_user(self, telephone, password, username=None, **kwargs):
kwargs['is_superuser'] = False
return self._create_user(telephone=telephone, password=password, username=username, **kwargs)
def create_superuser(self, telephone, password, username=None, **kwargs):
kwargs['is_superuser'] = True
return self._create_user(telephone=telephone, password=password, username=username, **kwargs)
视图函数:
def inherit_view(request): telephone = '11111111111' password = '111111' username = 'piggy' user = User.objects.create_user(telephone=telephone, password=password, username=username) print(user.username) return HttpResponse('继承')
验证:
from django.contrib.auth import authenticate
def authenticate_view(request):
user = authenticate(request, username='18888888888', password='111111')
if user:
print('验证成功')
else:
print('验证失败')
return HttpResponse('验证界面')
继承AbstractBaseUser
场景:想要修改默认的验证方式, 且有些user默认字段不想要(比如first_name,last_name),可以使用此方法创建类
- 继承AbstractBaseUser。但是最好确保对django有一定了解, 因为要尽可能的模拟user模型,否则可能出现隐患
- 继承PermissionsMixin, 用于用户权限
- AbstractBaseUser只包含了password加密算法和is_active,last_login记录
创建模型
继承AbstractBaseUser和PermissionsMixin
USERNAME_FIELD:用于验证的字段
REQUIRED_FIELD: 用命令行创建超级管理员时需要输入的字段, 如果是空列表,默认是需传USERNAME_FIELD和password
objects:模型的objects属性
尽量模拟AbstractUser的写法,防止出现未知错误
class User(AbstractBaseUser, PermissionsMixin): username = models.CharField(max_length=100, unique=True) telephone = models.CharField(max_length=11, unique=True) email = models.EmailField() is_active = models.BooleanField(default=0) USERNAME_FIELD = 'username' REQUIRED_FIELDS = [] objects = UserManager() def get_full_name(self): return self.username def get_short_name(self): return self.username
实现UserManager
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin class UserManager(BaseUserManager): def _create_user(self, telephone, email, password, username, **kwargs): if not telephone or not password: raise ValueError('The given telephone or email must be set') user = self.model(telephone=telephone, email=email, username=username, **kwargs) user.set_password(password) user.save() return user def create_user(self, telephone, email, password, username, **kwargs): kwargs['is_superuser'] = False return self._create_user(telephone=telephone, email=email, password=password, username=username, **kwargs) def create_superuser(self, telephone, email, password, username, **kwargs): kwargs['is_superuser'] = True return self._create_user(telephone=telephone, email=email, password=password, username=username, **kwargs)
验证
from django.contrib.auth import authenticate def authenticate_view(request): user = authenticate(request, username='18888888899', password='123456') if user: print(user.username) else: print('失败') # user = User.objects.filter(telephone='18888888899').first() # print(user.check_password('123456')) return HttpResponse('验证界面')

浙公网安备 33010602011771号