Django自定义User模型、认证、权限控制

Django自带强大的User系统,为我们提供用户认证、权限、组等一系列功能,可以快速建立一个完整的后台功能。 但User模型并不能满足我们的需求,例如自带的User表中没有手机号码,而且对于国人来说表中的first_name和last_name并没有什么卵用,对于实际生产中灵活的用户表来说重写User模型是非常有必要的。

扩展User模型

扩展User模型有许多的方法:

1、Proxy继承:

代理继承,此方法只能够继承User本身拥有的字段,并不能够添加和删改,不会影响表中原有的结构,但是可以定义一些方法来获取我们需要的数据:

1from django.contrib.auth.models import User 2class ProxyUser(User): 3 class Meta: 4 proxy = True # 定义代理模型 5 def get_data(self): 6 return self.objects.filter("过滤条件")
2、一对一外键:

如果我们对User本身的字段和验证方法没有要求,只是想要增加额外字段,可以通过创建另外一张表去关联User表,从而添加额外字段,并且我们可以写一个接受保存模型的信号处理方法,只要User调用了save方法,那么关联表就会自动添加一条数据与User新添加的用户进行绑定:

1from django.db import models 2from django.contrib.auth.models import User 3from django.dispatch import receiver # 导入receiver监听信号 4from django.db.models.signals import post_save # 导入post_save信号 5class ExtensionUser(object): 6 """创建一对一模型,并添加新的字段""" 7 user = models.OneToOneField(User,on_delete=models.CASCADE) 8 telephone = models.CharField(max_length=11,verbose_name="手机号码") 9 10@receiver(post_save,sender=User) # 监听到post_save事件且发送者是User则执行create_extension_user函数 11def create_extension_user(sender,instance,created,**kwargs): 12 """ 13 sender:发送者 14 instance:save对象 15 created:是否是创建数据 16 """ 17 if created: 18 # 如果创建对象,ExtensionUser进行绑定 19 ExtensionUser.objects.create(user=instance) 20 else: 21 # 如果不是创建对象,同样将改变进行保存 22 instance.extension.save()
3、继承AbstractUser自定义模型:

Django自带的User模型就是继承的AbstractUser类,因此我们可以通过继承AbractUser类自定义User模型:

1from django.contrib.auth.models import BaseUserManager,AbstractUser 2from shortuuidfield import ShortUUIDField # 使用shortuuid作为User表的主键,使数据更加的安全 3 4class UserManager(BaseUserManager): #自定义Manager管理器 5 def _create_user(self,username,password,email,**kwargs): 6 if not username: 7 raise ValueError("请传入用户名!") 8 if not password: 9 raise ValueError("请传入密码!") 10 if not email: 11 raise ValueError("请传入邮箱地址!") 12 user = self.model(username=username,email=email,**kwargs) 13 user.set_password(password) 14 user.save() 15 return user 16 17 def create_user(self,username,password,email,**kwargs): # 创建普通用户 18 kwargs['is_superuser'] = False 19 return self._create_user(username,password,email,**kwargs) 20 21 def create_superuser(self,username,password,email,**kwargs): # 创建超级用户 22 kwargs['is_superuser'] = True 23 kwargs['is_staff'] = True 24 return self._create_user(username,password,email,**kwargs) 25 26class User(AbstractUser): # 自定义User 27 GENDER_TYPE = ( 28 ("1","男"), 29 ("2","女") 30 ) 31 uid = ShortUUIDField(primary_key=True) 32 username = models.CharField(max_length=15,verbose_name="用户名",unique=True) 33 nickname = models.CharField(max_length=13,verbose_name="昵称",null=True,blank=True) 34 age = models.IntegerField(verbose_name="年龄",null=True,blank=True) 35 gender = models.CharField(max_length=2,choices=GENDER_TYPE,verbose_name="性别",null=True,blank=True) 36 phone = models.CharField(max_length=11,null=True,blank=True,verbose_name="手机号码") 37 email = models.EmailField(verbose_name="邮箱") 38 picture = models.ImageField(upload_to="Store/user_picture",verbose_name="用户头像",null=True,blank=True) 39 home_address = models.CharField(max_length=100,null=True,blank=True,verbose_name="地址") 40 card_id = models.CharField(max_length=30,verbose_name="身份证",null=True,blank=True) 41 is_active = models.BooleanField(default=True,verbose_name="激活状态") 42 is_staff = models.BooleanField(default=True,verbose_name="是否是员工") 43 date_joined = models.DateTimeField(auto_now_add=True) 44 45 USERNAME_FIELD = 'username' # 使用authenticate验证时使用的验证字段,可以换成其他字段,但验证字段必须是唯一的,即设置了unique=True 46 REQUIRED_FIELDS = ['email'] # 创建用户时必须填写的字段,除了该列表里的字段还包括password字段以及USERNAME_FIELD中的字段 47 EMAIL_FIELD = 'email' # 发送邮件时使用的字段 48 49 objects = UserManager() 50 51 def get_full_name(self): 52 return self.username 53 54 def get_short_name(self): 55 return self.username 56 57 class Meta: 58 verbose_name = "用户" 59 verbose_name_plural = verbose_name

自定义好User模型之后还需要在settings中设置系统才会识别当前User模型作为系统默认User模型,settings中需要先安装app,然后添加AUTH_USER_MODEL=‘app.User’:

之后重新python manage.py makemigrations,python manage.py migrate就可以使用该模型作为系统User模型了。

4、继承AbstractBaseUser类、PermissionsMixin类自定义User模型:

继承AbstracUser类自定义User有一个不好的地方,就是我们没有办法改变其已有的字段,比如first_name和last_name我们并不需要,这个时候就可以继承AbstractBaseUser和PermissionsMixin类完全重写User模型:

1from django.contrib.auth.models import AbstractBaseUser,PermissionsMixin,BaseUserManager 2from shortuuidfield import ShortUUIDField 3from django.db import models 4 5class UserManager(BaseUserManager): 6 def _create_user(self,username,password,email,**kwargs): 7 if not username: 8 raise ValueError("请传入用户名!") 9 if not password: 10 raise ValueError("请传入密码!") 11 if not email: 12 raise ValueError("请传入邮箱地址!") 13 user = self.model(username=username,email=email,**kwargs) 14 user.set_password(password) 15 user.save() 16 return user 17 18 def create_user(self,username,password,email,**kwargs): 19 kwargs['is_superuser'] = False 20 return self._create_user(username,password,email,**kwargs) 21 22 def create_superuser(self,username,password,email,**kwargs): 23 kwargs['is_superuser'] = True 24 kwargs['is_staff'] = True 25 return self._create_user(username,password,email,**kwargs) 26 27class User(AbstractBaseUser,PermissionsMixin): # 继承AbstractBaseUser,PermissionsMixin 28 GENDER_TYPE = ( 29 ("1","男"), 30 ("2","女") 31 ) 32 uid = ShortUUIDField(primary_key=True) 33 username = models.CharField(max_length=15,verbose_name="用户名",unique=True) 34 nickname = models.CharField(max_length=13,verbose_name="昵称",null=True,blank=True) 35 age = models.IntegerField(verbose_name="年龄",null=True,blank=True) 36 gender = models.CharField(max_length=2,choices=GENDER_TYPE,verbose_name="性别",null=True,blank=True) 37 phone = models.CharField(max_length=11,null=True,blank=True,verbose_name="手机号码") 38 email = models.EmailField(verbose_name="邮箱") 39 picture = models.ImageField(upload_to="Store/user_picture",verbose_name="用户头像",null=True,blank=True) 40 home_address = models.CharField(max_length=100,null=True,blank=True,verbose_name="地址") 41 card_id = models.CharField(max_length=30,verbose_name="身份证",null=True,blank=True) 42 is_active = models.BooleanField(default=True,verbose_name="激活状态") 43 is_staff = models.BooleanField(default=True,verbose_name="是否是员工") 44 date_joined = models.DateTimeField(auto_now_add=True) 45 46 USERNAME_FIELD = 'username' 47 REQUIRED_FIELDS = ['email'] 48 EMAIL_FIELD = 'email' 49 50 objects = UserManager() 51 52 def get_full_name(self): 53 return self.username 54 55 def get_short_name(self): 56 return self.username 57 58 class Meta: 59 verbose_name = "用户" 60 verbose_name_plural = verbose_name

此时数据库中只会生成我们定义好的字段。 定义好了User模型我们就可以使用Django自带的登录验证和权限系统了。 首先注册功能: form.py

1from django.forms import Form 2from django.forms import fields 3from django.core.exceptions import ValidationError 4 5class RegisterForm(Form): 6 username = fields.CharField( 7 required=True, 8 min_length=3, 9 max_length=18, 10 error_messages={ 11 "required":"用户名不可以为空!", 12 "min_length":"用户名不能低于3位!", 13 "max_length":"用户名不能超过18位!" 14 } 15 ) 16 password1 = fields.CharField( 17 required=True, 18 min_length=3, 19 max_length=18, 20 error_messages={ 21 "required":"密码不可以空", 22 "min_length": "密码不能低于3位!", 23 "max_length": "密码不能超过18位!" 24 } 25 ) 26 password2 = fields.CharField(required=False) 27 email = fields.EmailField( 28 required=True, 29 error_messages={ 30 "required":"邮箱不可以为空!" 31 }, 32 ) 33 34 def clean_password2(self): 35 if not self.errors.get("password1"): 36 if self.cleaned_data["password2"] != self.cleaned_data["password1"]: 37 raise ValidationError("您输入的密码不一致,请重新输入!") 38 return self.cleaned_data

views.py

1from django.shortcuts import render 2from django.contrib.auth import get_user_model 3from .forms import * 4from django.http import JsonResponse 5 6User = get_user_model() # 获取User模型 7 8def register(request): 9 if request.method == "GET": 10 return render(request,"register.html") 11 else: 12 form = RegisterForm(request.POST) 13 if form.is_valid(): 14 username = form.cleaned_data["username"] 15 password = form.cleaned_data["password1"] 16 email = form.cleaned_data["email"] 17 username_exists = User.objects.filter(username=username).exists() 18 if username_exists: 19 return JsonResponse({"code":400,"message":"验证失败","data":{"username":"您输入的用户名已存在!","password1":"","password2":"","email":""}}) 20 email_exists = User.objects.filter(email=email).exists() 21 if email_exists: 22 return JsonResponse({"code": 400, "message":"验证失败","data":{"username": "","password1":"","password2":"", "email": "您输入的邮箱已存在!"}}) 23 User.objects.create_user(username=username,password=password,email=email) 24 return JsonResponse({"code": 200,"message":"验证通过", "data":{"username": "","password1":"","password2":"", "email": ""}}) 25 else: 26 return JsonResponse({"code":400,"message":"验证失败","data":{"username":form.errors.get("username"),"password1":form.errors.get("password1"),"password2":form.errors.get("password2"),"email":form.errors.get("email")}})

登录功能 form.py:

1from django.forms import Form 2from django.forms import fields 3 4class LoginForm(Form): 5 username = fields.CharField( 6 required=True, 7 min_length=3, 8 max_length=18, 9 error_messages={ 10 "required":"用户名不可以为空!", 11 "min_length":"用户名不能低于3位!", 12 "max_length":"用户名不能超过18位!" 13 } 14 ) 15 password = fields.CharField( 16 required=True, 17 error_messages={ 18 "required":"密码不可以空", 19 } 20 )

views.py:

1from django.shortcuts import render 2from .forms import * 3from django.http import JsonResponse 4from django.contrib.auth import authenticate 5from django.contrib.auth import login 6# 登录视图名称不能起成login,与自带login函数重名 7def loginView(request): 8 if request.method == "GET": 9 return render(request,"login.html") 10 else: 11 form = LoginForm(request.POST) 12 if form.is_valid(): 13 username = form.cleaned_data.get("username") 14 password = form.cleaned_data.get("password") 15 remember = int(request.POST.get("remember")) 16 user = authenticate(request,username=username,password=password) # 使用authenticate进行登录验证,验证成功会返回一个user对象,失败则返回None 17 # 使用authenticate验证时如果is_active为False也会返回None,导致无法判断激活状态, 18 # 此时可以在seetings中配置: 19 # AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend'] 20 if user and user.is_active: # 如果验证成功且用户已激活执行下面代码 21 login(request,user) # 使用自带的login函数进行登录,会自动添加session信息 22 request.session["username"] = username # 自定义session,login函数添加的session不满足时可以增加自定义的session信息。 23 if remember: 24 request.session.set_expiry(None) # 设置session过期时间,None表示使用系统默认的过期时间 25 else: 26 request.session.set_expiry(0) # 0代表关闭浏览器session失效 27 return JsonResponse({"code": 200,"message":"验证通过","data":{ "error":""}}) 28 elif user and not user.is_active: 29 return JsonResponse({"code": 400, "message": "用户未激活", "data": {"error": "该用户还没有激活,请<a href='#'>激活</a>"}}) 30 else: 31 return JsonResponse({"code": 400, "message": "验证失败", "data": {"error": "用户名或密码错误"}}) 32 else: 33 return JsonResponse({"code":400,"message":"用户名或密码格式错误","data":{"error":"用户名或密码错误"}})

退出功能

1from django.contrib.auth import logout 2from django.shortcuts import redirect 3# 视图名不能起成logout 4def logoutView(request): 5 logout(request) # 调用django自带退出功能,会帮助我们删除相关session 6 return redirect(request.META["HTTP_REFERER"])

此时我们就完成了通过自定义User模型实现注册登录以及退出一系列的功能,配合前端页面就可以完美实现了。

用户与权限管理

定义了用户模型,就不可避免的涉及到了用户权限问题,Django同样内置了Permission系统,该权限系统都是针对表或者模型级别的,比如某个模型上的数据可以进行增删改查,他不能够针对数据级别,比如某个表中的某条数据是否能够进行增删改查操作(如果要实现数据级别的,可以考虑使用django-guardian)。创建完一个模型后,针对该模型默认有增、删、改、查四种权限,权限存储了数据库中的auth_permission表中:

codename表示权限的名字,name表示权限的作用,content_type_id表示某张表的id,即用来绑定数据表的,他关联django_content_type这张表:

添加权限的两种方法:

1#通过定义模型来添加权限: 2class Address(models.Model): 3 """ 4 收货地址 5 """ 6 recv_address = models.TextField(verbose_name = "收货地址") 7 receiver = models.CharField(max_length=32,verbose_name="接收人") 8 recv_phone = models.CharField(max_length=32,verbose_name="收件人电话") 9 post_number = models.CharField(max_length=32,verbose_name="邮编") 10 buyer_id = models.ForeignKey(to=User,on_delete = models.CASCADE,verbose_name = "用户id") 11 class Meta: 12 permissions = ( 13 ("view_addresses", "查看地址"), 14 ) 15# 通过代码添加权限 16from django.contrib.auth.models import Permission,ContentType 17from .models import Address 18content_type = ContentType.objects.get_for_model(Address) 19permission = Permission.objects.create(name = "查看地址",codename = "view_addresses",content_type = content_type)

User模型和权限之间可以通过以下几种方式来进行管理: 1、user.user_permissions.set(permission_list):直接给定一个权限的列表。 2、user.user_permissions.add(permission,permission,...):一个个添加权限。 3、user.user_permissions.remover(permission,permission):一个个删除权限。 4、user.user_permissions.clear():清除权限 5、user.has_perm('<app_name>.<codename>'):判断是否拥有某个权限,权限参数是一个字符串,格式是app_name.codename。 6、user.get_all_permission():获得所有权限。 我们可以通过appname_user_user_permission这张表来查看某个用户是否拥有某项权限:

权限限定装饰器: 使用django.contrib.auth.decorators.permission_required可以很方便的检查某个用户是否拥有某项权限:

1from django.contrib.auth.decorators import permission_required 2@permission_required('Cart.view_cart',login_url="/login/",raise_exception=True) 3# 第一个参数代表权限,login_url表示没有登录的话需要跳转的页面,raise_exception表示没有权限是返回403错误,默认是False,会跳转到login_url指定的页面。 4def view_cart(request): 5 reture HttpResponse("您可以查看购物车")

分组 权限有很多,一个模型就最少有四个权限,如果一些用户拥有相同的权限,每次都需要重复添加是很不方便的,此时分组功能就可以帮我们解决这个问题,我们可以把一些权限归类,然后添加到某个分组中,之后再把和需要赋予这些权限的用户添加的这个分组中,就比较方便的进行管理了。分组我们使用的是django.contrib.auth.models.Group模型,每个用户组拥有id和name两个字段该模型在数据库被映射为auth_group数据表。 分组操作: 1、Group.objects.create(group_name):创建分组 2、group.permission:某个分组上的权限。多堆多的关系。

group.permission.add:添加权限。 group.permission.remove:移除权限。 group.permission.clear:清除所有权限。 user.get_group_permission():获取用户所属组的权限。 3、user.groups:某个用户上的所有分组。多对多的关系。 在模板中使用权限: 在settings.TEMPLATES.OPTIONS.context_processors下,因为添加了django.contrib.auth.context_processors.auth上下文处理器,因此可以在模板中直接通过 perms来获取用户的所有权限:

1{% if perms.View.view_cart %} 2 购物车详情 3{% endif %}

原文链接:https://blog.csdn.net/weixin_44951273/article/details/101028522

点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

springboot2.2和mybatis2.1的增删改查

直接结构图!(https://oscimg.oschina.net/oscnet/574820232583656659f583b60ba15706741.png)数据表CREATETABLE\user\(\id\int(32)NOTNULLAUTO\_INCREMENT,\user\_name\varc

TypeScript 真香系列——接口篇

接口带来了什么好处好处One——过去我们写JavaScriptJavaScript中定义一个函数,用来获取一个用户的姓名和年龄的字符串:constgetUserInfofunction(user){returnname:${user.name},age:${user

mysql设置时区

mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0