drf大回顾
1 drf入门规范
-前后端开发模式:混合,分离
-API接口:地址(url),请求方法(method),请求参数(request),返回值(response)
-postman的使用
-序列化和反序列化
-restful规范
-http响应状态码
-想在django中写符合规范的接口
-djangorestframework(drf):django的app
-快速使用
2 CBV源码分析
-路由中 视图类.as_viwe()--->django的View的as_viwe()---->执行结果 view内存地址----》请求来了,路由匹配成功----》view(request)---->return self.dispatch()--->View的dispatch---》通过反射,不同的请求会执行视图类中跟请求同名的方法
# 最终本质跟写fbv的执行流程一样
# 最终结论:什么请求方式,就会执行视图类中的什么方法
3 APIView执行流程
# APIView 继承了View
# 视图类:class BookView(APIView):
# 路由中 视图类.as_viwe()--->drf的APIView的as_viwe()--->调用父类的as_view,去除csrf---》View类的as_view内部的view闭包函数(加了个装饰器)---》view(request)---->return self.dispatch()---》APIView的dispatch()
# 以后视图类,只要继承APIView及其子类
# 装饰器的本质原理
@装饰器 # add=装饰器(add)
def add()
# 闭包函数
# 补充
类中:self
类中:super()
# 1 Request 类的对象 以后用的request的源码分析
-request.data
-request.query_params
-request.Files
-request.method,request.path....跟之前一样
-重写了魔法方法---》__getattr__---->对象.属性,不存在会触发---》反射了self._request
# 将老的request赋值给_request,然后包装一个新的request
# 2 序列化组件
-能做的事:
1. 序列化,序列化器会把模型对象(queryset,单个对象)转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request.data以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能
-Serialzier---》book表的先做序列化,使用
-定义序列化类
class BookSerializer(Serialzier):
name=serializer.CharField()
-使用序列化类
ser=BookSerializer(instance=qs,单个对象,many=True)
ser.data # 字典
-字段类:很多
ListField
DictField
-字段参数:反序列化校验,字段自己的规则
read_only
write_only
-反序列化校验
-字段自己
-局部钩子:validate_字段名
-全局钩子:validate
-反序列化保存
-校验过后,ser.save()--->判断instance是否存在,如果不存在,就是调用ser的create方法
-在序列化类中,重写create
-反序列化更新
-校验过后,ser.save()--->判断instance是否存在,如果存在,就是调用ser的update方法
-在序列化类中,重写update
-ModelSerializer的使用---》不需要一个个写字段,跟表模型有对应关系
-extra_kwargs
-其它跟Serializer一模一样
-序列化,定制返回格式
-source
-SerializerMethodField:写在序列化类中,配合一个 get_字段名的方法,返回什么,前端就看到什么
-在表模型中写:写方法,返回字典,列表,字符串---》在序列化类中可以使用ListField,DictField
#3 请求与响应
Request类的对象
-接受前端传入的编码格式:json,urlencoded,form-data
-局部配置
-全局配置
-Request的源码
Response类的对象
-前端看到的形式(浏览器,json)
-源码分析
-data
-headers
-status_code
# 4 视图组件
-两个视图基类:APIView,GenericAPIView
-GenericAPIView:
-5 个视图扩展列,必须配合GenericAPIView
-{'code':100,'msg':'成功'}
-基于它在封装成自己的5个视图扩展类,返回格式符合公司规范
-9个视图子类
-5个:5个视图扩展类+GenericAPIView
-List和Create的组合
-其它组合
-视图集
-ViewSetMixin:只要继承他,路由写法变了
-ViewSet:ViewSetMixin, views.APIView
-GenericViewSet:(ViewSetMixin, generics.GenericAPIView)
-ModelViewSet:
-1 继承了ModelViewSet,没增加一条记录,就干某个事,重写perform_create
-2 序列化使用一个序列化类,反序列化使用配置的那个序列化类
-3 自定义了一个方法:login,使用了action装饰器,让它取到的qs对象和序列化类跟配置的都不一样
-ReadOnlyModelViewSet
-视图类 :self 内部有request对象,还有个action,就是视图类中方法的字符串名
# 路由
# 5 认证,频率,权限
-认证:写个类,继承BaseAuthentication,重写authenticate方法,在里面做认证,如果认证通过返回两个值,如果认证失败,就抛异常-----》局部,全局配置
-权限:同上
-频率:写个类,继承SimpleRateThrottle,重写get_cache_key,返回什么,就以什么做限制[ip,用户id]---》类属性:scope---》配置文件配置----》全局和局部配
-频率类:写个类,继承BaseThrottle,重写allow_request,如果频率运行,就返回True,如果频率不允许就返回False
# 6 自己写频率,同样的ip,一分钟只能访问3次---》频率类
# 自定义的逻辑{ip1:[第二次访问时间,第一次访问时间],ip2:[第一次访问时间,],ip3:[当前时间]}
#(1)取出访问者ip
#(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
#(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
#(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
#(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
# 7 排序
-使用内置的即可,查询所有接口,才有排序,必须继承GenericAPIView
-在视图类中,配置filter_backends=[OrderingFilter],还需要配置属性:ordering_fields=[id,price]
-如果是继承APIView写排序,自己写
# 8 过滤
-内置:SearchFilter, 模糊查询
-第三方:djagno-filter
-自己写的:写一个类,继承BaseFilterBackend,重写filter_queryset,返回qs对象,返回的就是过来后的
# 9 分页
-三种分页方式:PageNumberPagination,LimitOffsetPagination,CursorPagination
-每个有些类属性控制:每页显示多少条,最多显示多少条,第几页。。。
-写个类,继承三个之一,配置在视图类上(必须继承GenericAPIView)
-继承APIView写分页
# 10 异常处理()
-全局异常处理----》统一返回格式
-写个函数:def custom_exception_handler(exc, context)---》调用了原来的exception_handler(只能处理drf的异常)---》返回两种情况:None,Response的对象
-配置在drf的配置文件
# 11 接口文档编写
-corapi自动生成
# 12 JWT认证
-base64
-是什么
-三段式
-开发重点:签发,认证
-登录接口
-认证类
-djangorestframework-jwt
-基于auth的user表快速签发
-基于内置的认证类认证,配合权限
-登录成功返回格式
-自定义用户表---》签发---》登录接口
-自定义用户表---》认证---》认证类
-扩写了auth的user表
-快速签发---》只能使用用户名密码,不能多方式登录
-自定义登录接口---》实现多方式登录
# rbac
# 补充:
魔法方法:类中使用__开头 __结尾的方法,它不需要主动调用,某种情况下会自动触发
object类身上的
序列化类源码分析之---》many参数的作用
-如果传了many=True,序列化多条----》得到的对象不是BookSerializer对象,而是ListSerialzier的对象中套了一个个的BookSerializer对象
-如果传了many=False,序列化单条---》得到的对象就是BookSerializer的对象
-类实例化得到对象,是谁控制的?
def __new__(cls, *args, **kwargs):
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
return super().__new__(cls, *args, **kwargs)
# 序列化
# 视图类
# 三大认证
# 排序,过滤,分页
# 全局异常处理
# jwt
继承BaseThrottle的频率类
import time
from rest_framework.throttling import BaseThrottle
time_num = 60
dic = {}
class NewThrottle(BaseThrottle):
def allow_request(self, request, view):
ip = request.META.get('REMOTE_ADDR')
if ip == None:
return True
if ip in dic:
dic[ip].insert(0,time.time())
else:
dic[ip] = [time.time()]
if len(dic[ip]) > 5:
time_d = dic[ip][5] - dic[ip][1]
dic[ip].pop()
if time_d > 60:
return True
else:
return False
else:
return True
继承SimpleRateThrottle的频率类
from rest_framework.throttling import SimpleRateThrottle
class MyThrottle(SimpleRateThrottle):
scope = 'lisi'
def get_cache_key(self, request, view):
ip = request.META.get('REMOTE_ADDR')
return ip
中间件写频率类
import time
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
time_dic = {}
class middleThrottle(MiddlewareMixin):
def process_request(self, request):
ip = request.META.get('REMOTE_ADDR')
if ip in time_dic:
time_dic[ip].append(time.time())
else:
time_dic[ip] = [time.time()]
if len(time_dic[ip]) > 5:
time_d = time_dic[ip][5] - time_dic[ip][1]
time_dic[ip].pop(0)
print(time_d)
if time_d > 60:
return None
else:
return HttpResponse("访问频繁还要等待%s秒" % (60 - int(time_d)))
基于API的过滤排序分页
# 过滤
from django.db.models import Q
from rest_framework.filters import BaseFilterBackend
class publishfilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
name = request.query_params.get('name')
addr = request.query_params.get('addr')
if name and addr:
queryset = queryset.filter(Q(name__contains=name) | Q(addr__contains=addr))
return queryset
elif name and (not addr):
queryset = queryset.filter(name__contains=name)
return queryset
elif addr and (not name):
queryset = queryset.filter(addr__contains=addr)
return queryset
else:
return queryset
# 排序
from rest_framework.filters import OrderingFilter
# 分页
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
class MyPageNumberPagination(PageNumberPagination):
page_size = 2 # 每页多少条
page_query_param = 'page' # 在地址中page=第几页
page_size_query_param = 'page_size' # 在地址中page_size是每页显示多少条
max_page_size = 6 # 每页最大多少条
# 视图
class PublishView(APIView):
ordering_fields = ['addr', 'id']
def get(self, request):
queryset = publish.objects.all()
filtration = publishfilter() # 实例化过滤类
queryset = filtration.filter_queryset(request, queryset, self) # 调用内部的过滤方法
ordering = OrderingFilter() # 实例化排序类,注意要给一个列表
queryset = ordering.filter_queryset(request, queryset, self) # 调用排序类里的排序方法
paginator = MyPageNumberPagination() # 实例化分页类
qs = paginator.paginate_queryset(queryset, request, self) # 调用分页方法
ser = PublishSerialzier(instance=qs, many=True)
# return paginator.get_paginated_response(ser.data) # 调用分页类的内置方法
return Response(OrderedDict([
('count', paginator.page.paginator.count),
('next', paginator.get_next_link()),
('previous', paginator.get_previous_link()),
('results', ser.data)
]))
全局异常处理
from rest_framework.response import Response
from rest_framework.views import exception_handler
def my_exception_handler(exc, context):
e = exception_handler(exc, context)
if e:
if isinstance(e.data, dict):
detail = e.data.get('detail')
else:
detail = e.data
return Response({"code": 999, "msg": detail})
else:
return Response({"code": 1000, "msg": str(exc)})
自定义登陆接口
# jwt认证
import jwt
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.serializers import jwt_decode_handler
from .models import *
"""
authentication_classes = [JSONWebTokenAuthentication] # 认证类,drf-jwt提供的
permission_classes = [IsAuthenticated]
"""
class JWTAuthentication(BaseAuthentication):
def authenticate(self, request):
if request.method == 'GET':
token = request.META.get('HTTP_TOKEN')
try:
payload = jwt_decode_handler(token)
user_id = payload.get('user_id')
user = User.objects.get(pk=user_id)
except jwt.ExpiredSignature:
raise AuthenticationFailed('token过期')
except jwt.DecodeError:
raise AuthenticationFailed('解码失败')
except jwt.InvalidTokenError:
raise AuthenticationFailed('token认证异常')
except Exception:
raise AuthenticationFailed('token认证异常')
return user, token
# 视图
# from rest_framework_jwt.utils import jwt_payload_handler
class Userview(ViewSet):
authentication_classes = [JWTAuthentication]
def check(self, request):
return Response({'username':request.user.username})
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username).first()
pwd = user.password
res = pbkdf2_sha256.verify(password, pwd)
if res:
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response(common_response(token, user, request))
else:
return Response(common_response(code=100, msg='用户名或者密码错误'))
自定义注册接口
class UserDetailView(ViewSet):
def reg(self, request):
username = request.data.get('username')
password = request.data.get('password')
email = request.data.get('email')
age = request.data.get('age')
phone = request.data.get('phone')
if not all((username, password, phone, email, age)):
raise APIException('用户名,密码,手机号,邮箱,年龄,均不能为空')
salt = uuid.uuid4()
new_pwd = pbkdf2_sha256.hash(password, rounds=200000, salt=salt.bytes)
User.objects.create(username=username, password=new_pwd, email=email, age=age, phone=phone)
return Response(common_response(msg='注册成功'))
标签:return,回顾,ip,request,---,time,序列化,DRF
From: https://www.cnblogs.com/juzixiong/p/17447412.html