实操学习——个人资料的录入、修改、密码的修改
- 一、个人资料的录入和修改
- 知识补充:装饰器
- 二、密码的修改
- 知识补充:docker的关闭与启动
一、个人资料的录入和修改
- 在users的app下创建一个用户详情表
from django.contrib.auth.models import User
from django.db import models
from utils.modelsMixin import ModelSetMixin
# Create your models here.
# 创建用户详情表
class UserDetail(ModelSetMixin): #继承自定义的ModelSetMixin
SEX_CHOICES = (
(0, '女'),
(1, '男'),
)
# 数据库字段不能存储图片,保存的是图片的路径,所以用TextField
avatar = models.TextField(null=True, blank=True, verbose_name='头像')
phone = models.CharField(null=True, blank=True, verbose_name='手机号', max_length=11, unique=True)
age = models.IntegerField(null=True, blank=True, verbose_name='年龄')
sex = models.IntegerField(null=True, blank=True, verbose_name='性别', choices=SEX_CHOICES)
birthday = models.DateField(null=True, blank=True, verbose_name='生日')
# 一对一关系,关联django自带的user表 ,DO_NOTHING什么都不做
user = models.OneToOneField(User, on_delete=models.DO_NOTHING)
class Meta:
db_table = 'user_detail'
verbose_name = '用户详情'
verbose_name_plural = verbose_name
- 编写序列化器
from rest_framework.serializers import ModelSerializer
from .models import *
class UserDetailSerializer(ModelSerializer):
class Meta:
model = UserDetail
exclude = ['is_delete']
extra_kwargs = {
'avatar': {'read_only': True},
'user': {'required': False, 'write_only': True}
}
class UserSerializer(ModelSerializer):
# 关联系列化
userdetail = UserDetailSerializer(required=False, read_only=True)
class Meta:
model = User
exclude = ['password', 'last_name', 'user_permissions']
extra_kwargs = {
'id': {'read_only': True},
'last_login': {'read_only': True},
'is_superuser': {'read_only': True},
'is_staff': {'read_only': True},
'is_active': {'read_only': True},
'date_joined': {'read_only': True},
'groups': {'read_only': True}
}
- 配置路由
from django.urls import path
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token
from .views import *
urlpatterns = [
path('login/', obtain_jwt_token),
path('image/verification/<uuid:uuid>/', ImageVerifyView.as_view())
]
router = DefaultRouter()
router.register('users', UserViewSet)
urlpatterns += router.urls
- 编写views.py视图函数
import io
from django.contrib.auth.models import User
from django.http import HttpResponse
from django.shortcuts import render
# Create your views here.
from django.views import View
from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework.status import HTTP_404_NOT_FOUND
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework.permissions import IsAuthenticated
from config.dbs.redis_dev import LOGIN_KEY_TEMPLATE, EXPIRE_TIME
from utils.permission import ActiveUserPermission, wrap_permisssion, TeacherPermission
from utils.verifyUtil import ImageVerify
from .serializers import *
class ImageVerifyView(View):
"""
图片验证码视图类
get
返回图片验证码
"""
def get(self, request, uuid):
"""
返回验证码图片
:param request: 请求对象
:param uuid: 客户端的唯一标识
:return:
"""
imgVerify = ImageVerify()
img, code = imgVerify.verify_code()
# 将图片数据返回给客户端,无需持久化,所以用流,因为是图片,是字节数据,所以用字节流
img_bytes = io.BytesIO()
img.save(img_bytes, format='PNG')
image_bytes = img_bytes.getvalue() # 图片的字节数据
# print(code) # 如何保存 uuid:ZFhA key-value 临时数据 缓存在redis中
cache = get_redis_connection(alias='verify_codes')
cache.set(LOGIN_KEY_TEMPLATE % uuid, code, EXPIRE_TIME)
return HttpResponse(image_bytes, content_type='image/png')
class UserViewSet(ReadOnlyModelViewSet):
"""
update:
更新用户个人信息
前端传入的数据格式:
{
'first_name':'张三',
'email':'zs@maqujiaoyu.com'
'userdetail':
{
'age':18,
'sex':1
}
}
"""
queryset = User.objects.filter(is_active=1)
serializer_class = UserSerializer
# 登录后才可调用该接口
permission_classes = [IsAuthenticated]
@wrap_permisssion(TeacherPermission)
def list(self, request, *args, **kwargs):
return ReadOnlyModelViewSet.list(self, request, *args, **kwargs)
@wrap_permisssion(ActiveUserPermission)
def retrieve(self, request, *args, **kwargs):
return ReadOnlyModelViewSet.retrieve(self, request, *args, **kwargs)
@wrap_permisssion(ActiveUserPermission)
def update(self, request, pk):
"""
创建用户详情信息
"""
# 得到用户 user
try:
user = self.get_queryset().get(id=pk)
except User.DoesNotExist:
return Response(status=HTTP_404_NOT_FOUND)
# 得到用户输入的数据进行反序列化
user_serializer = self.get_serializer(user, data=request.data)
# 校验
user_serializer.is_valid(raise_exception=True)
# 修改user表信息
user_serializer.save() # 修改instance,data
# 判断用户是否传入详情信息
user_detail = request.data.get('userdetail') # 获取用户传入的详情数据
if user_detail:
# 有,判断用户是否有详情信息,判断数据库里面有没有 hasattr内置函数,判断属性是否存在
if hasattr(user, 'userdetail'):
# 如果有,则修改
user_detail_serializer = UserDetailSerializer(user.userdetail, data=user_detail) #修改传instance
else:
# 如果没有,则创建
user_detail['user'] = pk
user_detail_serializer = UserDetailSerializer(data=user_detail) #创建不传instance
user_detail_serializer.is_valid(raise_exception=True)
user_detail_serializer.save()
user_serializer.data['userdetail'] = user_detail_serializer.data # 字典的赋值 dict[key] = value
# 返回修改后的数据
return Response(user_serializer.data)
- 重写permission方法
设置操作的用户必须是当前登录的用户,确保只能修改自己的个人信息
1) 在utils文件夹下创建permission.py文件
2)编写类ActiveUserPermission
from functools import update_wrapper
from django.contrib.auth.models import Group
from rest_framework.permissions import BasePermission
class TeacherPermission(BasePermission):
def has_permission(self, request, view):
user = request.user # 当前请求的用户信息
# 判断身份,查询用户在不在老师这个分组里面
group = Group.objects.filter(name='老师').first()
user_groups = user.groups.all()
return user.is_superuser or group in user_groups
class ActiveUserPermission(BasePermission):
def has_permission(self, request, view):
# 操作的用户必须是当前登陆的用户
user = request.user
return user.id == int(view.kwargs['pk']) #判断当前用户的id等于传入进来的id
# 更改权限装饰器
def wrap_permisssion(*permissions, validate_permisssion=True):
def decorator(func):
def wrapper(self, request, *args, **kwargs):
self.permission_classes = permissions
if validate_permisssion:
self.check_permissions(request)
return func(self, request, *args, **kwargs)
return update_wrapper(wrapper, func)
return decorator
3)在views.py文件中使用
- 方法一
from utils.permission import ActiveUserPermission, wrap_permisssion, TeacherPermission
class UserViewSet(ReadOnlyModelViewSet):
"""
update:
更新用户个人信息
前端传入的数据格式:
{
'first_name':'张三',
'email':'zs@maqujiaoyu.com'
'userdetail':
{
'age':18,
'sex':1
}
}
"""
queryset = User.objects.filter(is_active=1)
serializer_class = UserSerializer
# 登录后才可调用该接口,并且操作用户必须是当前登录用户
permission_classes = [IsAuthenticated,ActiveUserPermission]
- 方法二
在permission.py中定义权限的装饰器
from functools import update_wrapper
# 更改权限装饰器 *permissions元组不定长参数
# validate_permisssion=True 是否要进行权限的校验
def wrap_permisssion(*permissions, validate_permisssion=True): #定义装饰器,接收装饰器参数
def decorator(func): #定义函数,接收原本的函数
def wrapper(self, request, *args, **kwargs): #定义函数,接收原本函数传入的参数
self.permission_classes = permissions #改变permission_classes为传入进来的permissions参数
if validate_permisssion: #判断是否需要权限的校验
self.check_permissions(request) #进行校验
return func(self, request, *args, **kwargs) # 再调用原本的函数
return update_wrapper(wrapper, func) #使用update_wrapper将升级后的函数伪装为原本的函数
return decorator
使用 @wrap_permisssion(ActiveUserPermission)
import io
from django.contrib.auth.models import User
from django.http import HttpResponse
from django.shortcuts import render
# Create your views here.
from django.views import View
from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework.status import HTTP_404_NOT_FOUND
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework.permissions import IsAuthenticated
from config.dbs.redis_dev import LOGIN_KEY_TEMPLATE, EXPIRE_TIME
from utils.permission import ActiveUserPermission, wrap_permisssion, TeacherPermission
from utils.verifyUtil import ImageVerify
from .serializers import *
class UserViewSet(ReadOnlyModelViewSet):
"""
update:
更新用户个人信息
前端传入的数据格式:
{
'first_name':'张三',
'email':'zs@maqujiaoyu.com'
'userdetail':
{
'age':18,
'sex':1
}
}
"""
queryset = User.objects.filter(is_active=1)
serializer_class = UserSerializer
# 登录后才可调用该接口
permission_classes = [IsAuthenticated]
@wrap_permisssion(TeacherPermission)
def list(self, request, *args, **kwargs):
return ReadOnlyModelViewSet.list(self, request, *args, **kwargs)
@wrap_permisssion(ActiveUserPermission)
def retrieve(self, request, *args, **kwargs):
return ReadOnlyModelViewSet.retrieve(self, request, *args, **kwargs)
@wrap_permisssion(ActiveUserPermission)
def update(self, request, pk):
"""
创建用户详情信息
"""
# 得到用户 user
try:
user = self.get_queryset().get(id=pk)
except User.DoesNotExist:
return Response(status=HTTP_404_NOT_FOUND)
# 得到用户输入的数据进行反序列化
user_serializer = self.get_serializer(user, data=request.data)
# 校验
user_serializer.is_valid(raise_exception=True)
# 修改user表信息
user_serializer.save() # 修改instance,data
# 判断用户是否传入详情信息
user_detail = request.data.get('userdetail') # 获取用户传入的详情数据
if user_detail:
# 有,判断用户是否有详情信息,判断数据库里面有没有 hasattr内置函数,判断属性是否存在
if hasattr(user, 'userdetail'):
# 如果有,则修改
user_detail_serializer = UserDetailSerializer(user.userdetail, data=user_detail) #修改传instance
else:
# 如果没有,则创建
user_detail['user'] = pk
user_detail_serializer = UserDetailSerializer(data=user_detail) #创建不传instance
user_detail_serializer.is_valid(raise_exception=True)
user_detail_serializer.save()
user_serializer.data['userdetail'] = user_detail_serializer.data # 字典的赋值 dict[key] = value
# 返回修改后的数据
return Response(user_serializer.data)
知识补充:装饰器
装饰器:在不改变函数本身的情况下进行功能的升级,增加
定义装饰器:装饰器的本质就是闭包,即函数的嵌套
"""
装饰器函数格式
def 装饰器名(接收原本函数的参数):
def 升级后的函数():
return 升级后的函数
使用装饰器: @装饰器名
"""
def decorator(func): #定义一个装饰器函数,定义func参数接收原本的函数
def wrapper(): #定义升级后的函数,可以接收原本函数的参数
print('权限的校验') #升级后的函数的功能
func() #执行原本的函数,已经作为参数传入到func中,直接调用
return wrapper #返回升级后的函数体
@decorator #使用装饰器 调用装饰器函数,把返回值赋值给原本的函数 等同于test = decorator(test)
def test():
print(''实现查询功能)
test() # wrapper()
装饰器传参
def wrap_permission(permissions): #再包裹一层函数,用于接收使用装饰器时传入的参数
def decorator(func): #定义一个装饰器函数,定义func参数接收原本的函数
def wrapper(): #定义升级后的函数
print('权限的校验',permissions) #升级后的函数的功能
func() #执行原本的函数,已经作为参数传入到func中,直接调用
return wrapper #返回升级后的函数体
return decorator
@wrap_permission(ActiveUserPermission) #使用装饰器 调用装饰器函数,把返回值赋值给原本的函数 等同于test = decorator(test)
def test():
print(''实现查询功能)
test() # wrapper()
二、密码的修改
在user的views.py中添加如下接口:
@action(methods=['put'], detail=True)
def password(self, request, pk):
# 得到要操作的用户对象
try:
user = self.get_queryset().get(id=pk)
except User.DoesNotExist:
return Response(status=HTTP_404_NOT_FOUND)
# 检查原密码是否正确
password = request.data['password'] # 得到客户端输入的原密码
# 确认新密码与新密码在前端进行校验
new_password = request.data['new_password'] # 得到客户端输入的新密码
# 不能使用user.password,这个是加密的密码,不能与前端获取的原密码进行比较
if user.check_password(password):
# 正确 修改密码
#set_password保存的是加密后的密码 ,不能用user.password=new_password,保存的是明文
user.set_password(new_password)
user.save()
return Response({'msg': '修改成功'})
else:
# 错误 返回提示,原密码错误
return Response(status=HTTP_400_BAD_REQUEST)
如果在后端校验新密码与确认新密码是否一致,可以修改如下:
1) 增加如下序列化器
#没有对应的模型,继承serializers.Serializer,有对应的模型,可以直接继承ModelSerializer
class UpdatePasswordSerializer(serializers.Serializer):
password = serializers.CharField(max_length=60, label='原密码')
new_password = serializers.CharField(max_length=60, label='新密码')
re_new_password = serializers.CharField(max_length=60, label='确认新密码')
#在序列化器中增加密码的校验
def validate(self, attrs):# 使用对象级别的校验
if attrs['new_password'] != attrs['re_new_password']:
raise serializers.ValidationError('两次密码输入不一致')
return attrs
2) 重写serializer
class UserViewSet(ReadOnlyModelViewSet):
queryset = User.objects.filter(is_active=1)
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
# 重写serializer
def get_serializer(self, *args, **kwargs):
if self.action == 'password': #如果调用的函数是password,返回UpdatePasswordSerializer
return UpdatePasswordSerializer(*args, **kwargs)
return self.serializer_class(*args, **kwargs)
3)修改密码时增加序列化器校验
@action(methods=['put'], detail=True)
def password(self, request, pk):
# 得到要操作的用户对象
try:
user = self.get_queryset().get(id=pk)
except User.DoesNotExist:
return Response(status=HTTP_404_NOT_FOUND)
# 检查原密码是否正确
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
password = request.data['password'] # 得到客户端输入的原密码
# 确认新密码与新密码在前端进行校验
new_password = request.data['new_password'] # 得到客户端输入的新密码
# 不能使用user.password,这个是加密的密码,不能与前端获取的原密码进行比较
if user.check_password(password):
# 正确 修改密码
#set_password保存的是加密后的密码 ,不能用user.password=new_password,保存的是明文
user.set_password(new_password)
user.save()
return Response({'msg': '修改成功'})
else:
# 错误 返回提示,原密码错误
return Response(status=HTTP_400_BAD_REQUEST)
知识补充:docker的关闭与启动
关闭所有的docker容器(不是删除容器):
sudo docker kill `sudo docker ps -q`
开启所有的docker容器
sudo docker start `sudo docker ps -ap`