本小节主要推导出auth模块是如何使用的,以及一些前置内容。
auth_user表:
在创建好一个django项目之后,直接执行数据库迁移命令,会自动生成很多表,如django_session,本小节将介绍另外一个很重要的表auth_user
从字段名可以看出,这张表储存的就是用户相关的一些信息。
那么auth_user这张表怎么使用呢?
如何创建超级用户和普通用户呢?
创建超级用户(管理员)
python manage.py createsuperuser
创建好了之后,可以看到密码字段是密文的(data_joined字段的值不是参考当前时间的,所以会对不上)
后面用户的登陆、注销、校验等功能,全都都依靠auth_user这张表来完成。
创建普通用户
稍后补充
汇总一览:
语法 | 作用 |
---|---|
authenticate | 对用户名和密码进行校验 |
login | 保存用户状态 |
user | 获取当前用户对象 |
is_authenticated() | 校验当前用户是否经过校验 |
login_required | 装饰器需要通过校验才能访问的视图函数 |
check_oassword | 用于做密码校验 |
set_password、save | 修改密码及数据库更新操作 |
logout | 注销 |
create_user、create_superuser | 注册 |
逐步推导:
书写一个登陆页面,视图层层以外的代码略。
def login(request):
if request.method == 'POST':
name = request.POST.get('uname')
passwd = request.POST.get('upassword')
# 去auth_user表中校验数据
return render(request,'login.html')
这个时候就遇到了问题:
方法1:authenticate
作用: 自动查找auth_user表,对用户名和密码进行校验,其中自动给密码加密再比对。
代码示例:
from django.contrib import auth
def login(request):
if request.method == 'POST':
name = request.POST.get('uname')
passwd = request.POST.get('upassword')
# 去auth_user表中校验数据
auth.authenticate(request,username=name,password=passwd)
return render(request,'login.html')
'''
username和password为auth_user表中对应的字段名
'''
注意事项:括号内必须同时传入用户名和密码,不能只传用户名。
返回值:
校验通过的情况下: 返回用户数据对象,该对象的类中定义了双下str方法,打印对象可以获取到该用户数据对象的用户名,也就是username字段的值。
进入到准备好的Login页面,输入一章节中注册的超级用户,测试校验结果。
from django.contrib import auth def login(request): if request.method == 'POST': name = request.POST.get('uname') passwd = request.POST.get('upassword') res = auth.authenticate(request,username=name,password=passwd) print(res) print(res.username) print(res.password) return render(request,'login.html')
可以看到,authenticate方法的返回值就是登陆用户的数据对象,可以通过“.字段名”的形式,来获取该对象的信息,比如用户名和密码,当然了密码还是加密的。
校验不通过的情况下: 返回None(演示略)
方法2:login
**作用:**保存用户状态,类似于request.session。
代码示例:
from django.contrib import auth
def login(request):
if request.method == 'POST':
name = request.POST.get('uname')
passwd = request.POST.get('upassword')
user_obj = auth.authenticate(request,username=name,password=passwd)
if user_obj:
auth.login(request,user_obj)
return redirect('/userlist')
return render(request,'login.html')
方法3:user
**作用:**保存用户状态之后,可以在任意地方使用request.user来获取当前登陆认证的用户数据对象。
代码示例:
from django.contrib import auth
def login(request):
if request.method == 'POST':
name = request.POST.get('uname')
passwd = request.POST.get('upassword')
user_obj = auth.authenticate(request,username=name,password=passwd)
if user_obj:
auth.login(request,user_obj)
return redirect('/userlist')
return render(request,'login.html')
def get_userlist(request):
data_queryset = models.User.objects.all()
# 获取当前登陆用户对象在auth_user表中的username字段值
current_user = request.user.username
return render(request,'userlist/userlist.html',locals())
方法4:is_authenticated()
**作用:**用于校验用户数据对象是否是经过校验的,没登录是匿名用户AnonymousUser,所以该方法就是用来快速判断的,返回值为bool。
代码示例:
print(request.user.is_authenticated())
'''
返回值为bool
'''
方法5:login_required
**作用:**装饰器函数,被装饰的函数必须要通过校验才可以访问。
代码示例:
from django.contrib.auth.decorators import login_required
@login_required(login_url='/login/')
def get_userlist(request):
return render(request,'userlist/userlist.html',locals())
login_required装饰器函数需要传入参数login_url,用于指定当用户没有登陆校验时,跳转到哪个页面,如上图中的登陆页面。
如果很多个视图函数都需要进行装饰,那么推荐使用全局配置。
局部配置
@login_required(login_url='/login/')
全局配置
''' settings文件 ''' LOGIN_URL = '/login/' ''' 视图 ''' @login_required def get_userlist(request): return render(request,'userlist/userlist.html',locals())
如果局部和全局都有配置,以局部为准,优先级局部大于全局。
方法6:check_password
**作用:**用于做密码对比校验。
代码推导及示例:
给后台页面书写一个用户修改密码的功能
def change_password(request): old_password = request.POST.get('old_password') new_password = request.POST.get('new_password') # 校验老的密码对不对 # 如果输入的老密码正确,那就准备修改成新密码 # 保存到数据库 return render(request,'change_password.html',locals())
此时会遇到一个问题,由于表单上提交的数据后端接收后都是明文的,而auth_user表中是加密的密文,所以无法直接进行比对
前文中提到的authenticate方法虽然可以进行密码校验,但是需要传入用户名,直接传入密码进行校验是不行的,而这是修改密码也不会再去获得用户名,所以就衍生出了check_oassword方法。
- 将输入的老密码与数据库中的密码进行比对成功之后,下一步就是将老密码替修改成新的密码,如何设置见set_password
格式: request.user.check_password( 新密码 ):
@login_required
def set_password(request):
#
if request.is_ajax():
if request.method == 'POST':
back_dic = {'code':1003,'msg':''}
oldpassword = request.POST.get('oldpassword')
newpassword = request.POST.get('newpassword')
confirmpassword = request.POST.get('confirmpassword')
# 校验老密码是否一致
if request.user.check_password(oldpassword):
# 如果老密码正确就对比两次输入的新密码
if newpassword == confirmpassword:
#两次密码也都一致,那就操作数据库
request.user.set_password(newpassword)
request.user.save()
back_dic['msg'] = '修改成功'
return JsonResponse(back_dic)
else:
back_dic['code'] = 1005
back_dic['msg'] = '两次新密码不一致'
return JsonResponse(back_dic)
else:
# 如果不正确就返回错误信息
back_dic['code'] = 1004
back_dic['msg'] = '原密码错误'
return JsonResponse(back_dic)
方法7:set_password、save
**作用:**用于修改auth_user表中的密码,前者用于修改,后者用于操作数据库完成最终的修改操作。
示例:
request.user.set_password('新密码')
request.user.save()
'''
不执行save操作是改变不了数据库中的数据。
'''
方法8:logout
**作用:**注销(清除session)。
示例:
def logout(request):
auth.logout(request)
return render(request,'index.html')
方法9:create_user、create_superuser
**作用:**创建用户,auth_user表中新增数据。前者用于创建普通用户,后者用户创建超级用户(了解即可)。
示例:
from django.contrib.auth.models import User #导入表
def register(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 操作auth_user表写入数据
User.objects.create_user(username=username,password=password)
return render(request,'login.html')
return render(request,'register.html')
'''
create_superuser 创建超级用户,了解即可,因为也不会暴露给用户去创建。
与命令行不同,代码创建必须要传入邮箱。
'''
为什么要使用专门的create_user,而不用ORM章节中的create方法呢?
原生的auth_user中有很多字段,但是如果现在就想要一个phone字段来存储用户的手机号,或者想要一个reg_time字段来存储注册的时间,那这个时候该怎么办呢。
推导:
下列为导入auth模块和auth_user表的代码,点开User表的源码。
from django.contrib import auth
from django.contrib.auth.models import User
可以发现,User类中什么字段都没有,都在继承的AbstractUser类当做,所以我们可以通过继承内置的 AbstractUser 类,来定义一个自己的Model类。
配置:
在应用下面的model.py文件中,自定义类。
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
# 扩展的自定义字段
phone = models.BigIntegerField()
如果继承了AbstractUser类,那么在执行数据库迁移命令的时候,auth_user表就不会再创建出来了,而UserInfo表中会出现auth_user所有的字段,外加自己扩展的字段,这样就可以直接点击自己的表,更加快速的完成操作及扩展,就不用再导入auth_user表了。
settings配置
AUTH_USER_MODEL = '应用名.表名(不是类名)' # 直接应用名.类 中间不要加别的, 不是类名,所以不能大写,app01.User会报错
前提:
在继承之前没有执行过数据库迁移命令,也就是说auth_user没有被创建,如果当前库已经创建了那么就需要重新换一个库,所以需要在数据库的设计阶段就要确定好,是否要进行扩展。
继承的类里面不要覆盖AbstractUser里面的字段名,表里面有的字段都不要动,只扩展额外字段即可。
需要在配置文件中告诉django,要用自定义表替代auth_user,不配置会报错,配置如下:
AUTH_USER_MODEL = '应用名.表名(类名)'
'''
直接“应用名.表名”,不要再中间再加一个models
另外:
配置好之后,先执行数据库迁移,不然会报错:
ValueError: Dependency on app with no migrations:应用名
'''
当上述的操作都完成,并且具备了前提条件的情况下,就可以执行数据库迁移命令了(具体命令见ORM章节)。
注:
当我们书写了自己的表,代替了auth_user,那么auth模块的功能还是可以照常使用,但是auth_user表不需要导入,改为了自定义的表,并且在使用auth模块的功能时,原本是User的都需要改成自己的表,如:“UserInfo”
以注册功能为例,因为需要引入auth_user表进行新增数据的操作,当我们更换了表,如:“UserInfo”之后代码就变成了:
# from django.contrib.auth.models import User # 由于替换成了UserInfo表,所以就不需要导入User表了。 from 应用名.models import UserInfo def register(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 操作auth_user表写入数据,由于替换成了Userinfo,所以原来的User要进行修改 # User.objects.create_user(username=username,password=password) UserInfo.objects.create_user(username=username,password=password) return render(request,'login.html') return render(request,'register.html')
在扩展了auth模块的user表之后,需要先执行数据库迁移命令,然后再启动django项目。
直接启动就会报这个错误
python manage.py makemigrations
python manage.py migrate