|
# 作业:
1 自定义User表,新增mobile唯一约束字段;新增icon图片字段
2 在自定义User表基础上,用 GenericViewSet + CreateModelMixin + serializer 完成User表新增接口(就是注册接口)(重要提示:序列化类要重写create方法,不然密码就是明文了)
3 在自定义User表基础上,用 GenericViewSet + RetrieveModelMixin + serializer 完成User表单查(就是用户中心)
4 在自定义User表基础上,用 GenericViewSet + UpdateModelMixin + serializer 完成用户头像的修改
[urls路由层]
from django.contrib import admin
from django.urls import path,re_path,include
from api import views
from rest_framework.routers import SimpleRouter
router=SimpleRouter()
router.register('register',views.RegisterView,'register')
urlpatterns = [
# path('register/', views.RegisterView.as_view({'post':'create'})),
path('',include(router.urls)),# 第二种自动生成路由
]
urlpatterns+=router.urls # 一种方式自动生成路由
[View视图层]
from django.shortcuts import render
from rest_framework.viewsets import GenericViewSet
# ViewSetMixin:重写了as_view,路由配置变样了 ,generics.GenericAPIView:只需要配置两东西
from rest_framework.mixins import CreateModelMixin,RetrieveModelMixin,UpdateModelMixin
from api import models
from api import ser
class RegisterView(GenericViewSet,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin): # 因为路由是自动生成的我们这里配置了各种mixin代表着路由自动生成action={'get'='list'}...方法
queryset = models.User.objects.all()
serializer_class = ser.UserModelSerializer
# 假设get请求和post请求,用的序列化类不一样,如何处理
# 重写get_serializer_class,返回啥,用的序列化类就是啥
# 注册用的序列化类UserModelSerializer,查询一个用的序列化类是UserReadOnlyModelSerializer
def get_serializer_class(self):
print(self.action) # create,retrieve
if self.action == 'create':
return ser.UserModelSerializer
elif self.action == 'retrieve':
return ser.UserReadOnlyModelSerializer
elif self.action == 'update': # 更具不同的序列化器返回不同字段逻辑
return ser.UserImageModelSerializer
[ser.py序列化器]
from rest_framework import serializers
from api import models
from rest_framework.exceptions import ValidationError
class UserModelSerializer(serializers.ModelSerializer):
re_password = serializers.CharField(max_length=16, min_length=4, required=True,
write_only=True) # 因为re_password在表中没有,需要在这定义,而且反序列话不用这个字段给客户看,所以用write_only
class Meta:
model = models.User
fields = ['username', 'password', 'mobile', 're_password', 'icon']
extra_kwargs = {
'username': {'max_length': 16},
'password': {'write_only': True}
}
# 局部钩子
def validate_mobile(self, data):
if not len(data) == 11:
raise ValidationError('手机号不合法')
return data
def validate(self, attrs):
if attrs.get('password') == attrs.get('re_password'):
raise ValidationError('两次密码不一致')
attrs.pop('re_password') # 这个字段是自己写的,数据库没有存,我们存放是的时候是**attr直接打散的
return attrs
def create(self, validated_data):
# 如果上面没有剔除re_password可以在validated_date剔除也乐意
user = models.User.objects.create(**validated_data)
return user
class UserReadOnlyModelSerializer(serializers.ModelSerializer):
class Meta:
models = models.User
fields = ['username', 'icon']
---------------------------------------------------------------------------------------------------------------------------# 1、jwt
[1、控制用户登录后才能访问,不登陆不能访问]
# 可以通过认证类:JSONWebTokenAuthentication和权限类IsAuthenticated,来控制用户登录以后才能访问某一些接口
# 如果用户不登录可以访问,只需要把权限类IsAuthenticated注释掉就可以了
class OrderAPIView(APIView):
authentication_class = [JSONWebTokenAuthentication]# 这个负载里面必须有jwt才会进行解析后面的token,要不然后面decode方法时request.user没有东西
# 权限控制
permission_classes = [IsAuthenticated]
def get(self,request,*args,**kwargs):
return Response('这是订单信息')
class UserInfoAPIView(APIView):
authentication_class = [JSONWebTokenAuthentication]# 内置的jwt匿名用户和登录用户都可以访问
# 权限控制 在不加这个权限控制的时候匿名用户也可以访问的
# permission_classes = [IsAuthenticated]
def get(self,request,*args,**kwargs):
return Response('这是订单信息')
[2、控制登录接口返回的数据格式]
-第一种方案,自己写登录接口
-第二种写法,用内置,控制登录接口返回的数据格式
-jwt的配置信息中有这个属性
'JWT_RESPONSE_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_response_payload_handler',
-重写jwt_response_payload_handler,配置成咱们自己的
-----------------------------------------------------------------------------------------
from rest_framework.authentication import BaseAuthentication # 基于它
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication # 基于它
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
import jwt
from api import models
# 3、1自定义BaseAuthentication(自定义认证方式)
class MyJwtAuthentication(BaseAuthentication):
def authenticate(self, request):
jwt_value=request.META.get('HTTP_AUTHORIZATION')
if jwt_value:
# jwt提供了通过三段token,取出payload的方法,并且有校验功能
try:
payload=jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
raise AuthenticationFailed('签名过期')
except jwt.InvalidTokenError:
raise AuthenticationFailed('用户非法')
except Exception as e:
raise AuthenticationFailed(str(e))
print(payload)
# 需要返回用户对象 第一种,去数据库查,完成信息都存在的
# user=models.User.objects.get(pk=payload.get('user_id'))
# 第二种不查库 速度快,但是对象的信息都是自己手动放进去的,是一个餐费
user= models.User(id=payload.get('user_id'),username=payload.get('username'))
return user,jwt_value
# 没有值,直接抛异常
raise AuthenticationFailed('你没有携带认证信息')
# 3、2 基于BaseJSONWebTokenAuthentication内置写好的
class MyJwtAuthentication1(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
jwt_value=request.META.get('HTTP_AUTHORIZATION')
if jwt_value:
# jwt提供了通过三段token,取出payload的方法,并且有校验功能
try:
payload=jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
raise AuthenticationFailed('签名过期')
except jwt.InvalidTokenError:
raise AuthenticationFailed('用户非法')
except Exception as e:
raise AuthenticationFailed(str(e))
print(payload)
user=self.authenticate_credentials(payload)
return user, jwt_value
# 没有值,直接抛异常
raise AuthenticationFailed('你没有携带认证信息')
----------------------------------------------------------------------------------------------------
# 4、多种方式登录、手动签发token
1、使用用户名,手机号,邮箱,都可以登录
# 使用用户名,手机号,邮箱,都可以登录#
# 前端需要传的数据格式
{
"username":"lqz/1332323223/33@qq.com",
"password":"lqz12345"
}
# 视图
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin, ViewSet
from app02 import ser
class Login2View(ViewSet): # 跟上面完全一样
def login(self, request, *args, **kwargs):
# 1 需要 有个序列化的类
login_ser = ser.LoginModelSerializer(data=request.data,context={'request':request})
# 2 生成序列化类对象
# 3 调用序列号对象的is_validad
login_ser.is_valid(raise_exception=True)
token=login_ser.context.get('token')
# 4 return
return Response({'status':100,'msg':'登录成功','token':token,'username':login_ser.context.get('username')})
# 序列化类(写主要业务逻辑)
from rest_framework import serializers
from api import models
import re
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler
class LoginModelSerializer(serializers.ModelSerializer):
username=serializers.CharField() # 重新覆盖username字段,数据中它是unique,post,认为你保存数据,自己有校验没过
class Meta:
model=models.User
fields=['username','password']
def validate(self, attrs):
print(self.context)
# 在这写逻辑
username=attrs.get('username') # 用户名有三种方式
password=attrs.get('password')
# 通过判断,username数据不同,查询字段不一样
# 正则匹配,如果是手机号
if re.match('^1[3-9][0-9]{9}$',username):
user=models.User.objects.filter(mobile=username).first()
elif re.match('^.+@.+$',username):# 邮箱
user=models.User.objects.filter(email=username).first()
elif:
user=models.User.objects.filter(username=username).first()
if user: # 存在用户
# 校验密码,因为是密文,要用check_password
if user.check_password(password):
# 签发token
payload = jwt_payload_handler(user) # 把user传入,得到payload
token = jwt_encode_handler(payload) # 把payload传入,得到token
self.context['token']=token
self.context['username']=user.username
return attrs
else:
raise ValidationError('密码错误')
else:
raise ValidationError('用户不存在')
-----------------------------------------------------------------------------
## 5 jwt的配置过期时间
# jwt的配置
import datetime
JWT_AUTH={
'JWT_RESPONSE_PAYLOAD_HANDLER':'app02.utils.my_jwt_response_payload_handler',
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 过期时间,手动配置
}
--------------------------------------------------------------------------------
# 2 基于角色的权限控制(django内置auth体系)
# RBAC :是基于角色的访问控制(Role-Based Access Control ),公司内部系统
# django的auth就是内置了一套基于RBAC的权限系统
# django中
# 后台的权限控制(公司内部系统,crm,erp,协同平台)
user表
permssion表
group表
user_groups表是user和group的中间表
group_permissions表是group和permssion中间表
user_user_permissions表是user和permission中间表
# 前台(主站),需要用三大认证
# 3 django缓存
# 前端混合开发缓存的使用
-缓存的位置,通过配置文件来操作(以文件为例)
-缓存的粒度:
-全站缓存
中间件
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
。。。。
'django.middleware.cache.FetchFromCacheMiddleware',
]
CACHE_MIDDLEWARE_SECONDS=10 # 全站缓存时间
-单页面缓存
在视图函数上加装饰器
from django.views.decorators.cache import cache_page
@cache_page(5) # 缓存5s钟
def test_cache(request):
import time
ctime=time.time()
return render(request,'index.html',context={'ctime':ctime})
-页面局部缓存
{% load cache %}
{% cache 5 'name' %} # 5表示5s钟,name是唯一key值
{{ ctime }}
{% endcache %}
# 前后端分离缓存的使用
- 如何使用
from django.core.cache import cache
cache.set('key',value可以是任意数据类型)
cache.get('key')
-应用场景:
-第一次查询所有图书,你通过多表联查序列化之后的数据,直接缓存起来
-后续,直接先去缓存查,如果有直接返回,没有,再去连表查,返回之前再缓存
|
|