Django之auth模块
auth模块是Django内置的用户权限管理模块:比如用户的创建,认证,登入,注销等。当我们创建一个APP时,auth模块就会自动添加在 INSTALLED_APPS=['django.contrib.auth',]
auth常用的几个方法:
User模型(auth/models.py中):用来维护用户信息的模型;比如用户的创建,认证等; 源码中User模型继承自AbstractUser,而AbstractUser继承自AbstractBaseUser,有以下字段:
username: 最长150个字符,必须传递
first_name:外国人的第一个名字
last_name: 外国人的最后一个名字
email: 邮箱
is_staff: 是否为员工,可以进入后台管理系统
is_active: 是否是可用的。对于一些想要删除账号的数据,我们设置这个值为False
就可以了,而不是真正的从数据库中删除。
date_joined: 账号创建的时间。
objects = UserManager(): 定义objes方法(两个方法一个是创建普通用户:create_user()和创建一个超级用户create_superuser())
EMAIL_FIELD = "email"
USERNAME_FIELD = "username" 唯一验证身份的标志
REQUIRED_FIELDS = ["email"]
而AbstractBaseUser中的字段有:(它中的字段和方法对于User和AbstractUser都可以用)
password :密码,经过哈希处理的
last_login:最后登入时间
is_active = True :默认账号是可用状态
常用的几个方法:
get_username() :返回用户名
save():保存
set_password():设置密码
check_password():检验密码是否正确
User模型的基本使用方法:
使用User模型之前我们需要先把模型映射的数据库中即(makemigrations,migrate),数据库中的auth_user表就是保存用户信息的。
上代码:
from django.shortcuts import render,HttpResponse from django.contrib.auth.models import User from django.contrib.auth import authenticate def index(request): User.objects.create_user("李四","zhangsan@163.com","123456") #创建一个普通用户,参数可以只传递一个用户名,其他的可传可不传 User.objects.create_superuser("里斯","lisi@163.com","111111") #创建一个超级用户,三个参数用户名,邮箱,密码必须传递 user = User.objects.first() user.set_password("qweqwe") #修改密码,修改之后必须保存 user.save() user = authenticate(request,username = "李四",password = "123456") #验证这个用户存不存在,如果存在返回这个用户的用户名 if user: print("有这个用户") else: print("没有这个用户") return HttpResponse("ok")
User模型虽然已经很强大了,但是有时依然不能满足我们的需求,比如我们想再定义几个字段,或者添加一些方法什么的,这个时候我们就需要扩展User模型,方法有以下四种:
User模型的扩展一之proxy模型:
使用范围:我们对它的字段很满意了没有其他可以添加,只是需要再添加一些方法,比如想获取所有的超级管理员;
注意:这种方式只能添加方法,不能添加字段,除了添加的这个方法之外和User没有区别
app/models.py中的代码:
from django.contrib.auth.models import User class getsuperuser(User): #定义一个类继承自User类 class Meta: proxy = True #设置代理模式 @classmethod def get_superuser(cls): #定义一个类方法(获取所有的管理员) return cls.objects.filter(is_superuser=True)
app/views.py中的代码:
from django.shortcuts import render,HttpResponse from django.contrib.auth.models import User from .models import getsuperuser #导入这个模块 userlist = getsuperuser.get_superuser() #获取所有的管理 for user in userlist: print(user) userlist01 = User.objects.all() print(userlist01) userlist02 = getsuperuser.objects.all() #在这种代理模式中User模型和代理模型的使用是一样的即Uer.objects.all()等价于getsuperuser.objects.all() print(userlist02) return HttpResponse("ok")
User模型的扩展二之一对一外键:(这种方式是创建一个新的表,用来保存一些新的字段并且和User表一一对应)
使用范围:如果我们要添加一个字段和方法
app/models.py中的代码:
from django.db import models from django.contrib.auth.models import User from django.dispatch import receiver from django.db.models.signals import post_save class UserExtension(models.Model): #定义一个User一对一的外键类 user = models.OneToOneField(User,on_delete=models.CASCADE,related_name="extension") #外键User telephone = models.CharField(max_length=11,unique=True) school = models.CharField(max_length=100) @receiver(post_save,sender = User) #定义一个信号机制,信号的发送者sender是User,信号是post_save,当User这个实例被创建的时候同时创建一个user的外键,否则保存这个实例和外键 def handle_user_extenson(sender,instance,created,**kwargs): if created: //如果User被创建了,那么同时创建一个UserExtension,并且外键与User对应 UserExtension.objects.create(user=instance) else: instance.extension.save() //如果User被保存,则UserExtension也保存
app/views.py中代码:
user = User.objects.create_user("王二","wanger@163.com","123123") #创建一个用户 user.extension.telephone = "18899998888" #给这个用户添加手机号 user.extension.school = "北京交通大学" #给这里用户添加一个学校 user.save()
User模型的扩展三之继承自AbstractUser:
使用范围:如果我们不想改变原来User的内容,又想在数据库中的同一个表中添加一些新的字段
User本身也是继承这个AbstractUser模型写的,相当于我们在原User的基础上重新写了User
因为objects这个方法也在这个User模型中,所以我们可以重新定义objects;
注意:(1)这种方式破坏了User数据表的结构,所以必须在第一次migrate之前定义好(2)在setting中设置:AUTH_USER_MODEL = 'front.User'(这里的front是你的app)
app/models.py中的代码
rom django.db import models from django.contrib.auth.models import User from django.contrib.auth.models import AbstractUser,AbstractBaseUser,BaseUserManager class UserManager(BaseUserManager): #重新定义一个UserManager def _create_user(self,telephone,username,password,**kwargs): if not telephone: raise ValueError("必须要有手机号") if not password: raise ValueError("必须要有密码") user = self.model(telephone=telephone,username=username,**kwargs) user.set_password(password) user.save() return user def create_user(self,telephone,username,password,**kwargs): kwargs["is_superuser"] = False return self._create_user(telephone=telephone,username=username,password=password,**kwargs) def create_superuser(self,telephone,username,password,**kwargs): kwargs["is_superuser"] = True return self._create_user(telephone=telephone,username=username,password=password,**kwargs) class User(AbstractUser): telephone = models.CharField(max_length=11,unique=True) school = models.CharField(max_length=100) #指定telephone作为USERNAME_FIELD,以后使用authenticate;函数验证的时候,就可以根据telephone来验证;而不是原来的username USERNAME_FIELD = 'telephone' REQUIRED_FIELDS = []
objects = UserManager() # 重新定义Manager对象,在创建user的时候使用telephone,username和password,而不是使用username和password
app中views.py中的代码
from django.shortcuts import render,HttpResponse from django.contrib.auth import authenticate from .models import User #这里需要导入的是models下面的User(注意) def index(request): telephone = '18899996666' password = '111111' username = '张三' User.objects.create_superuser(telephone=telephone,username=username,password=password) user = authenticate(request,username = "18899996666",password = "111111") #这里的username并不是真正的username而是代表USERNAME_FIELD的值,使用手机号验证,返回的是一个手机号 print(user) //返回的是一个手机号
print(user.username) //返回的是一个用户名 return HttpResponse("ok")
User模型的扩展四之继承自AbstractBaseUser:(这时其实就相当于在写一个AbstractUser)
使用范围:如果既想改变验证方式,又想添加字段,还不想要User原来的字段,可以完全重新写一个User;继承自AbstractBaseUser
注意(和上面一样):(1)这种方式破坏了User数据表的结构,所以必须在第一次migrate之前定义好(2)在setting中设置:AUTH_USER_MODEL = 'front.User'(这里的front是你的app)
app/models.py中的代码
from django.db import models from django.contrib.auth.models import User from django.contrib.auth.models import AbstractUser,AbstractBaseUser,BaseUserManager,PermissionsMixin class UserManager(BaseUserManager): #和上面一样重新定义了bjects def _create_user(self,telephone,username,password,**kwargs): if not telephone: raise ValueError("必须要有手机号") if not password: raise ValueError("必须要有密码") user = self.model(telephone=telephone,username=username,**kwargs) user.set_password(password) user.save() return user def create_user(self,telephone,username,password,**kwargs): kwargs["is_superuser"] = False return self._create_user(telephone=telephone,username=username,password=password,**kwargs) def create_superuser(self,telephone,username,password,**kwargs): kwargs["is_superuser"] = True return self._create_user(telephone=telephone,username=username,password=password,**kwargs) class User(AbstractBaseUser,PermissionsMixin): #定义User继承在AbstractBaseUser,PermissionsMixin(其实这时相当于在写一个AbstractUser) telephone = models.CharField(max_length=11,unique=True) username = models.CharField(max_length=10,unique=True) USERNAME_FIELD = "telephone" REQUIRED_FIELDS = [] objects = UserManager() def get_full_name(self): return self.username def get_short_name(self): return self.username
app/views.py中的代码:
from django.shortcuts import render,HttpResponse from django.contrib.auth import authenticate from .models import User def index(request): telephone = '18899996666' password = '111111' username = '张三' User.objects.create_superuser(telephone=telephone,username=username,password=password) user = authenticate(request,telephone = "18899996666",password = "111111") print(user) return HttpResponse("ok")
login/logout模块和登入限制:
from django.shortcuts import render,HttpResponse,redirect,reverse from django.contrib.auth import authenticate,login,logout from .models import User from .forms import LoginForm from django.contrib.auth.decorators import login_required def my_login(request): //登入 if request.method == "GET": //如果是get请求则返回登入页面 return render(request,"login.html") if request.method == "POST": //如果是post请求,则交给表单验证 form = LoginForm(request.POST) if form.is_valid(): telephone = form.cleaned_data.get("telephone") password = form.cleaned_data.get("password") remember = form.cleaned_data.get("remember") user = authenticate(request,telephone=telephone,password=password) //验证 if user and user.is_active: login(request,user) //登入 if remember: //设置session request.session.set_expiry(None) else: request.session.set_expiry(0) next_url = request.GET.get("next") //如果有next,则直接跳转到这个页面(限制登入时django自动添加了next参数) if next_url: return redirect(next_url) else: return HttpResponse("登入成功") else: return HttpResponse("用户名或者密码错误") else: print(form.errors.get_json_data()) return redirect(reverse("login")) def my_logout(request): logout(request) //退出登入 return HttpResponse("退出成功") @login_required(login_url="/login/") //限制登入装饰器,login_url表示失败后去哪个页面 def my_file(request): return HttpResponse("个人中心")
form.py
from django import forms from django.contrib.auth import get_user_model //获取User模型(这个方法是或者setting中的AUTH_USER_MODEL = "login.User") class LoginForm(forms.ModelForm): remember = forms.BooleanField(required=False) telephone = forms.CharField(max_length=11) class Meta: model = get_user_model() fields = ["password"] //这里不能添加telephone,因为Model.Form会把这个手机号去数据库中验证,如果有直接报错,所以telephone自己定义一个 error_messages={ "telephone":{ "required":"手机号必须有" }, "password":{ "required": "手机号必须有" } }
权限
添加权限两种方式第一种是模型中添加权限:
class Article(models.Model): author = models.ForeignKey(get_user_model(),on_delete=models.CASCADE) title = models.CharField(max_length=20) content = models.CharField(max_length=100) class Meta: permissions = [ ("black_article","拉黑文章") ]
第二种方式代码中添加权限:
def add_permissions(request): content_type = ContentType.objects.get_for_model(Article) Permission.objects.create(name="可编辑的权限",codename="edit_article",content_type=content_type) return HttpResponse("添加成功")
User模型和模型权限之间的管理
def operate_permissions(request): user = User.objects.first() content_type = ContentType.objects.get_for_model(Article) // print(content_type.id) permissions = Permission.objects.filter(content_type=content_type) //获取这个模型所有的权限 for permission in permissions: print(permission) # user.user_permissions.set(permissions) #一次性添加多个权限 # user.user_permissions.clear() #一次性清楚所有权限 user.user_permissions.add(permissions[0],permissions[1]) //一次添加一个权限 user.user_permissions.add(*permissions) user.user_permissions.remove(permissions[1]) //一次移除一个权限 if user.has_perm("login.aa_article"): //检查一次是否拥有某个权限,格式是app_name.codename print("有这个权限") permissions_all = user.get_all_permissions() //获取user所有的权限 print(permissions_all) return HttpResponse("添加成功")
权限装饰器(规定必须拥有什么样的权限才能访问视图函数)
from django.contrib.auth.decorators import permission_required //导入装饰器,这个装饰器做了两件事(一件事验证是否登入,另一件事验证是否有权限) @permission_required("login.add_article",login_url="/login/",raise_exception=True) //规定只有add_article的权限才能访问这个视图函数也可以是一个列表["login.add_article","login.view_article"];login_url表示没有权限的时候跳转的页面,raise_exception=True表示没有权限的时候跳转到403页面 def add_article(request): return HttpResponse("添加文章的页面")
权限分组:
def operate_group(request): # group = Group.objects.create(name="财务") #添加一个分组 # content_type = ContentType.objects.get_for_model(Article) # permissions = Permission.objects.filter(content_type=content_type) //获得这个模型的所有权限 group = Group.objects.filter(name="财务").first() //获得第一个分组 # group.permissions.set(permissions) //给这个分组添加权限 # group.save() //保存 user = User.objects.first() # user.groups.add(group) //把这个user添加到这个组 # user.save() # permissions = user.get_group_permissions() //获得所在组的所有权限 # print(permissions) if user.has_perm("login.add_article"): print("有这个权限") #has_perm(),先去判断user里面有没有这个权限,如果没有再去所在分组中查找 return HttpResponse("分组")
模板中使用权限:有一些页面只想展示给有权限的人看,比如有关财务的页面只给具有财务权限的人看。
#因为setting.TEMPLATS.OPTIONS.context_processors下添加了django.contrib.auth.context_processors.auth上下文处理器,所以在模板中可以直接通过perms来获取用户的所有权限
{% if perms.login.add_article %} <p>添加文章</p> {% endif %}