您的位置:首页技术文章
文章详情页

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

【字号: 日期:2024-09-10 13:53:50浏览:2作者:猪猪

Django自带强大的User系统,为我们提供用户认证、权限、组等一系列功能,可以快速建立一个完整的后台功能。

但User模型并不能满足我们的需求,例如自带的User表中没有手机号码,而且对于国人来说表中的first_name和last_name并没有什么卵用,对于实际生产中灵活的用户表来说重写User模型是非常有必要的。

扩展User模型

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

1、Proxy继承:

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

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

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

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

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

from django.contrib.auth.models import BaseUserManager,AbstractUserfrom shortuuidfield import ShortUUIDField # 使用shortuuid作为User表的主键,使数据更加的安全class UserManager(BaseUserManager): #自定义Manager管理器 def _create_user(self,username,password,email,**kwargs):if not username: raise ValueError('请传入用户名!')if not password: raise ValueError('请传入密码!')if not email: raise ValueError('请传入邮箱地址!')user = self.model(username=username,email=email,**kwargs)user.set_password(password)user.save()return user def create_user(self,username,password,email,**kwargs): # 创建普通用户kwargs[’is_superuser’] = Falsereturn self._create_user(username,password,email,**kwargs) def create_superuser(self,username,password,email,**kwargs): # 创建超级用户kwargs[’is_superuser’] = Truekwargs[’is_staff’] = Truereturn self._create_user(username,password,email,**kwargs)class User(AbstractUser): # 自定义User GENDER_TYPE = (('1','男'),('2','女') ) uid = ShortUUIDField(primary_key=True) username = models.CharField(max_length=15,verbose_name='用户名',unique=True) nickname = models.CharField(max_length=13,verbose_name='昵称',null=True,blank=True) age = models.IntegerField(verbose_name='年龄',null=True,blank=True) gender = models.CharField(max_length=2,choices=GENDER_TYPE,verbose_name='性别',null=True,blank=True) phone = models.CharField(max_length=11,null=True,blank=True,verbose_name='手机号码') email = models.EmailField(verbose_name='邮箱') picture = models.ImageField(upload_to='Store/user_picture',verbose_name='用户头像',null=True,blank=True) home_address = models.CharField(max_length=100,null=True,blank=True,verbose_name='地址') card_id = models.CharField(max_length=30,verbose_name='身份证',null=True,blank=True) is_active = models.BooleanField(default=True,verbose_name='激活状态') is_staff = models.BooleanField(default=True,verbose_name='是否是员工') date_joined = models.DateTimeField(auto_now_add=True) USERNAME_FIELD = ’username’ # 使用authenticate验证时使用的验证字段,可以换成其他字段,但验证字段必须是唯一的,即设置了unique=True REQUIRED_FIELDS = [’email’] # 创建用户时必须填写的字段,除了该列表里的字段还包括password字段以及USERNAME_FIELD中的字段 EMAIL_FIELD = ’email’ # 发送邮件时使用的字段 objects = UserManager() def get_full_name(self):return self.username def get_short_name(self):return self.username class Meta:verbose_name = '用户'verbose_name_plural = verbose_name

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

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

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

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

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

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

from django.contrib.auth.models import AbstractBaseUser,PermissionsMixin,BaseUserManagerfrom shortuuidfield import ShortUUIDFieldfrom django.db import modelsclass UserManager(BaseUserManager): def _create_user(self,username,password,email,**kwargs):if not username: raise ValueError('请传入用户名!')if not password: raise ValueError('请传入密码!')if not email: raise ValueError('请传入邮箱地址!')user = self.model(username=username,email=email,**kwargs)user.set_password(password)user.save()return user def create_user(self,username,password,email,**kwargs):kwargs[’is_superuser’] = Falsereturn self._create_user(username,password,email,**kwargs) def create_superuser(self,username,password,email,**kwargs):kwargs[’is_superuser’] = Truekwargs[’is_staff’] = Truereturn self._create_user(username,password,email,**kwargs)class User(AbstractBaseUser,PermissionsMixin): # 继承AbstractBaseUser,PermissionsMixin GENDER_TYPE = (('1','男'),('2','女') ) uid = ShortUUIDField(primary_key=True) username = models.CharField(max_length=15,verbose_name='用户名',unique=True) nickname = models.CharField(max_length=13,verbose_name='昵称',null=True,blank=True) age = models.IntegerField(verbose_name='年龄',null=True,blank=True) gender = models.CharField(max_length=2,choices=GENDER_TYPE,verbose_name='性别',null=True,blank=True) phone = models.CharField(max_length=11,null=True,blank=True,verbose_name='手机号码') email = models.EmailField(verbose_name='邮箱') picture = models.ImageField(upload_to='Store/user_picture',verbose_name='用户头像',null=True,blank=True) home_address = models.CharField(max_length=100,null=True,blank=True,verbose_name='地址') card_id = models.CharField(max_length=30,verbose_name='身份证',null=True,blank=True) is_active = models.BooleanField(default=True,verbose_name='激活状态') is_staff = models.BooleanField(default=True,verbose_name='是否是员工') date_joined = models.DateTimeField(auto_now_add=True) USERNAME_FIELD = ’username’ REQUIRED_FIELDS = [’email’] EMAIL_FIELD = ’email’ objects = UserManager() def get_full_name(self):return self.username def get_short_name(self):return self.username class Meta:verbose_name = '用户'verbose_name_plural = verbose_name

此时数据库中只会生成我们定义好的字段。

定义好了User模型我们就可以使用Django自带的登录验证和权限系统了。

首先注册功能:

form.py

from django.forms import Formfrom django.forms import fieldsfrom django.core.exceptions import ValidationErrorclass RegisterForm(Form): username = fields.CharField(required=True,min_length=3,max_length=18,error_messages={ 'required':'用户名不可以为空!', 'min_length':'用户名不能低于3位!', 'max_length':'用户名不能超过18位!'} ) password1 = fields.CharField(required=True,min_length=3,max_length=18,error_messages={ 'required':'密码不可以空', 'min_length': '密码不能低于3位!', 'max_length': '密码不能超过18位!'} ) password2 = fields.CharField(required=False) email = fields.EmailField(required=True,error_messages={ 'required':'邮箱不可以为空!'}, ) def clean_password2(self):if not self.errors.get('password1'): if self.cleaned_data['password2'] != self.cleaned_data['password1']:raise ValidationError('您输入的密码不一致,请重新输入!') return self.cleaned_data

views.py

from django.shortcuts import renderfrom django.contrib.auth import get_user_modelfrom .forms import *from django.http import JsonResponseUser = get_user_model() # 获取User模型def register(request): if request.method == 'GET':return render(request,'register.html') else:form = RegisterForm(request.POST)if form.is_valid(): username = form.cleaned_data['username'] password = form.cleaned_data['password1'] email = form.cleaned_data['email'] username_exists = User.objects.filter(username=username).exists() if username_exists: return JsonResponse({'code':400,'message':'验证失败','data':{'username':'您输入的用户名已存在!','password1':'','password2':'','email':''}}) email_exists = User.objects.filter(email=email).exists() if email_exists:return JsonResponse({'code': 400, 'message':'验证失败','data':{'username': '','password1':'','password2':'', 'email': '您输入的邮箱已存在!'}}) User.objects.create_user(username=username,password=password,email=email) return JsonResponse({'code': 200,'message':'验证通过', 'data':{'username': '','password1':'','password2':'', 'email': ''}})else: 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:

from django.forms import Formfrom django.forms import fieldsclass LoginForm(Form): username = fields.CharField(required=True,min_length=3,max_length=18,error_messages={ 'required':'用户名不可以为空!', 'min_length':'用户名不能低于3位!', 'max_length':'用户名不能超过18位!'} ) password = fields.CharField(required=True,error_messages={ 'required':'密码不可以空',} )

views.py:

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

退出功能

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

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

用户与权限管理

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

创建完一个模型后,针对该模型默认有增、删、改、查四种权限,权限存储了数据库中的auth_permission表中:

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

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

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

添加权限的两种方法:

#通过定义模型来添加权限:class Address(models.Model): ''' 收货地址 ''' recv_address = models.TextField(verbose_name = '收货地址') receiver = models.CharField(max_length=32,verbose_name='接收人') recv_phone = models.CharField(max_length=32,verbose_name='收件人电话') post_number = models.CharField(max_length=32,verbose_name='邮编') buyer_id = models.ForeignKey(to=User,on_delete = models.CASCADE,verbose_name = '用户id') class Meta:permissions = ( ('view_addresses', '查看地址'),)# 通过代码添加权限from django.contrib.auth.models import Permission,ContentTypefrom .models import Addresscontent_type = ContentType.objects.get_for_model(Address)permission = Permission.objects.create(name = '查看地址',codename = 'view_addresses',content_type = content_type)

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

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>.’):判断是否拥有某个权限,权限参数是一个字符串,格式是app_name.codename。

6、user.get_all_permission():获得所有权限。

我们可以通过appname_user_user_permission这张表来查看某个用户是否拥有某项权限:

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

权限限定装饰器:

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

from django.contrib.auth.decorators import permission_required@permission_required(’Cart.view_cart’,login_url='/login/',raise_exception=True)# 第一个参数代表权限,login_url表示没有登录的话需要跳转的页面,raise_exception表示没有权限是返回403错误,默认是False,会跳转到login_url指定的页面。def view_cart(request): 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来获取用户的所有权限:

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持好吧啦网。如有错误或未考虑完全的地方,望不吝赐教。

标签: Django
相关文章: