郑统京 发表于 2023-2-12 12:51:58

Django核心(drf总结+代码思路)

# 1、restful规范---10条
# 2、django上写符合restful规范的接口
# 3、drf写接口
# 4、APIView--》继承了原生View--》get,post方法
    -(为什么get请求来了,就会执行get方法:原生View)
    -路由配置:视图类.as_view()--->view(闭包函数)的内存地址
    -请求来了,就会执行view(request,(路由有参数)分组分出的字段,默认传过来的字段)---》self.dispatch()--》处理的

    -APIView重写了dispatch:
      包装了request对象,三大组件(权限、认证、频率)、分页、解析器、响应器、全局异常,去掉了csrf认证
# 补充HTTP版本1.0和2.0区别 :可以实现一次请求带多个http请求过去
# 5、Request对象:request._request,多了request.data方法三种格式数据都能接收,重写了__getattr__,request.method--->去原生的request中拿
    -前端传过来的数据从哪里取?
      -地址栏里:request.GET/query_params(drf)
      -请求体中的数据:request.data/POST(json合适解析不了)--->本质是request.body中取
      -请求头中数据:request.META.get(HTTP_变成大写key),为什么有的数据放到请求头中,get请求时在body内带不过去,地址栏不安全,所以才放到头里面例如jwt的认证
      -特殊取法:request.COOKIE/POST/GET比较常用,单独从request.body取了出来
# 6、Response对象--》封装了原生的HttpResponse,Response(data[这个放在响应体中是一个字典转给到前端为json格式],headers={},status=1/2/3/4/5开头的)
    -data存放在响应体中
    -header存放在响应头中
# 7、自己封装了Response对象
class APIResponse(Response):
    def __init__(self,code=100,msg='成功',data=None,status=None,headers=None,**kwargs):
      dic = {'code': code, 'msg': msg}
      ifdata:
            dic = {'code': code, 'msg': msg,'data':data}
      dic.update(kwargs)
      super().__init__(data=dic, status=status,headers=headers)
[用法]
return APIResponse(data={"name":'lqz'},token='dsafsdfa',aa='dsafdsafasfdee')
return APIResponse(data={"name":'lqz'})
return APIResponse(code='101',msg='错误',data={"name":'lqz'},token='dsafsdfa',aa='dsafdsafasfdee',header={})
# 8、序列化类:(第一重要)
    -serializer
      -写字段,字段名要和表的字段相对应,想不对应(source可以修改),有属性,read_only,write_only,max_length...
      -SerializerMethodField必须配套一个get_字段名,返什么,前端看到的就是什么
            authors=serializers.SerializerMethodField() #它需要有个配套方法,方法名叫get_字段名,返回值就是要显示的东西
            def get_authors(self,instance):
                # instance是book对象,为什么是book,在视图中要序列化放进来的对象
                authors=instance.authors.all()# 取出所有作者
                ll=[]
                for author in authors:
                  ll.append({'name':author.name,'age':author.age})
                return ll
    -ModelSerializer
      -class Meta:
            表对应
            取出字段(__all__,列表)            fields=[非数据库字段],非数据库字段只参与序列化不参与反序列化,比如model中的property方法字段写了进来等
            排除的字段(用的比较少,exclude)
            extra_kwargs会给字段的属性
      -重写某个字段(密码字段)
         password=serializers.SerializerMethodField()
            def get_password(self,instance):
                return "***"
      -校验:字段自己的校验,局部钩子,全局钩子
            -只要序列化类的对象执行了is_valid(),这些钩子都会走,可以在钩子里写逻辑
      -在表模型(model)中写方法,可以直接从上面面取出字段中直接写,但是不参与反序列化
    -序列化多条(many=True):本质,ListSerializer内部套了一个个serialzer对象
    -重写ListSerializer,让序列化对象和自己写的ListSerializer对应上,根据源码有一个字段class Meta中写_class=自己携带ListSerializer对象就可以了(了解)
    -序列化类(instance,data,many,context={'request':request})
      -视图函数中给序列化对象传递数据,使用context,传回来,,放进去直接使用序列化对象.context.get()
# 9、视图
    -两个视图基类 APIView,GenericAPIView(继承APIView):
      -涉及到数据库和系列化类的操作的时候,尽量用GenericAPIView
      -不涉及到数据库操作用APIView
    -5个视图拓展类(父类都是object)
      CreateModelMixin:create
      DestroyModelMixin:destory
      ListModelMixin:list
      RetrieveModelMixin:retrieve
      UpdateModelMixin:update
    -9个视图子类(GenericAPIView+视图拓展类的其中几个)
      RetrieveUpdateDestroyAPIView
      CreateAPIView
      RetrieveAPIView
      DestroyAPIView
      RetrieveUpdateAPIView
      ListCreateAPIView
      UpdateAPIView
      ListAPIView
      RetrieveDestroyAPIView
    -视图集
      -ModelViewSet:5大接口都有了(内部有5个Mixin方法和GeneriticViewSet方法)
      -ReadOnlyModelViewSet:获取一条和获取多条的接口,没有增删改,只有查
       -GenericViewSet:ViewSetMixin+GenericAPIView      
      ViewSet:ViewSetMixin+APIView
      ViewSetMixin:类重写了as_view,路由配置就变样了
# 10、路由
    -基本配置:跟之前一样
    -有action的,必须继承ViewSetMixin
    -自动生成:Default和SimpleRouter
      -四部曲:导入,实例化得到对象,注册多个,对象.urls(自动生成的路由)
            1、from rest_framework.routers import SimpleRouter
            2、router=SimpleRouter()
            3、router.register('register',views.RegisterView,'register')
            4、路由相加urlpatterns+=router.urls/include:path('',include(router.urls))
      -视图类中自定义的方法,如何自动生成路由
            -在自定义的方法上加装饰器(action)
            -@action(methods=['GET','POST'],detail=True)
            -两个参数method=,表示这两种请求都唔那个接受
            -两个参数detail=True,表示生成带pk的连接
# 11、三大认证(第二重要)
    -认证组件:校验用户是否登录
      写一个认证类,继承BaseAuthentication类(规范必须重写authenticate方法),重写authenticate,内部写认证逻辑,认证通过返回两个值,第一个是user(可以是对象也可以是自己生成user对象但是不完整节省资源)
    认证失败,抛出异常
      -全局使用,局部使用,局部禁用(中括号空值)
      -自定义认证类
            class MyAuthentication(BaseAuthentication):
                def authenticate(self, request):
                  # 认证逻辑,如果认证通过,返回两个值
                  #如果认证失败,抛出AuthenticationFailed异常
                  token=request.GET.get('token')
                  iftoken:
                        user_token=UserToken.objects.filter(token=token).first()
                        # 认证通过
                        if user_token:
                            return user_token.user,token这个就是为什么request.user可以拿到当前用户的对象,.user跨表到User用户对象里面去了]
                        else:
                            raise AuthenticationFailed('认证失败')
                  else:
                        raise AuthenticationFailed('请求地址中需要携带token')
------------------------------------------------------------------------------------------------------------------------
    -权限:校验用户是否有权限进行后续操作
      -写一个类继承BasePermission,重写has_permission,True和False(根据request.method方法判断)
      -自定义权限类
            class UserPermission(BasePermission):
            defhas_permission(self, request, view):
                # 不是超级用户,不能访问
                # 由于认证已经过了,request内就有user对象了,当前登录用户
                user=request.user# 当前登录用户
                # 如果该字段用了choice,通过get_字段名_display()就能取出choice后面的中文
                print(user.get_user_type_display())
                if user.user_type==1: 可以继续判断request.method方法在返回True
                  return True
                else:
                  return False
      -全局使用,局部使用,局部禁用(中括号空值)
------------------------------------------------------------------------------------------------------------------------
    -频率:限制用户访问频次
      -写一个类,不是直接继承BaseThrottle而是继承SimpleRateThrottle,重写get_cache_key,返回什么,就以谁做限制,scop=luffy字段,需要跟setting中的key对应 luffy:3/h(一小时访问三次1)
      -需求:发送短信验证码接口,一分钟只能发送一次,局部使用,配置视类上
      -自定义ip频率类
            class IPThrottle():
                #定义成类属性,所有对象用的都是这个
                VISIT_DIC = {}
                def __init__(self):
                  self.history_list=[]
                def allow_request(self, request, view):
                  '''
                  #(1)取出访问者ip
                  #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
                  #(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
                  #(4)判断,当列表小于3, 说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
                  #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
                  '''
                  ip=request.META.get('REMOTE_ADDR')
                  ctime=time.time()
                  if ip not in self.VISIT_DIC:
                        self.VISIT_DIC=
                        return True
                  self.history_list=self.VISIT_DIC   #当前访问者时间列表拿出来
                  while True:
                        if ctime-self.history_list[-1]>60:
                            self.history_list.pop() # 把最后一个移除
                        else:
                            break
                  if len(self.history_list)<3:
                        self.history_list.insert(0,ctime)
                        return True
                  else:
                        return False
                def wait(self):
                  # 当前时间,减去列表中最后一个时间
                  ctime=time.time()
                  return 60-(ctime-self.history_list[-1])
# 12、解析器:前端传的编码格式,能不能解析(默认三种全配了,基本不需要修改),可能你携带上传文件接口,局部配置一下,只能传form-data,局部使用:
# 13、响应器:响应的数据,是json还是带浏览器页面的那种(不需要配置),在settings中已经配置好了,根据user_agent判断不同的客户端类型返回不同的页面
# 14、过滤器:借助于第三方django-filter
    -注册应用
    -setting中配置DjangoFilterBackend或者局部配置
    -filter_filed={'age','sex'}
# 15、排序
    -全局或者局部配置rest_framework.filter,OrderingFilter
    -视图类中配置:ordering_fields={'id','age'}
# 16、分页
-使用:
    [继承了APIView的视图类中使用]
      自定义分页:
      class Mypage:
            page_size=2
            page_query_params='page'
      page=Mypage()
      # 在数据库中获取分页的数据
      page_list=page.paginate_queryset(queryset对象,request,view=self)
      # 对分页进行序列化
      ser=BookSerializer1(instance=page_list,many=True)
      # return Response(ser.data)
    [继承了视图子类的视图中使用]
         pagination_class = PageNumberPagination[(配置成自己重写的,可以修改字段)]
-
    cursor_query_param:默认查询字段,不需要修改
    page_size:每页数目
    ordering:按什么排序,需要指定
-
    default_limit 默认限制,默认值与PAGE_SIZE设置一直
    limit_query_param limit参数名,默认’limit’
    offset_query_param offset参数名,默认’offset’
    max_limit 最大limit限制,默认None
[-PageNumberPagination:最常用的,需要在setting中配置page_size,四个参数]
    page_size 每页数目
    page_query_param 前端发送的页数关键字名,默认为”page”
    page_size_query_param 前端发送的每页数目关键字名,默认为None
    max_page_size 前端最多能设置的每页数量
# 17、全局异常
-写一个方法
       def exception_handler(exc, context):
            # 走drf原来的异常,原来的异常有一些处理
            response = drf_exception_handler(exc, context)
         # 我们自己处理,drf没有处理,丢给django的异常
            if response is None:
                if isinstance(exc, DatabaseError):#处理了一下数据库错误
                  response = Response({'detail': '数据库错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
                else: # 全局异常统一这个处理
                  response = Response({'detail': '未知错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
                return response
                -配置文件中配置(以后所有drf的异常,都会走到这里)
                  REST_FRAMEWORK = {
                        'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
                  }
# 18 jwt(第一重要)
-是什么json web token新的认证方式
-三段:头,荷载(用户信息),签名
-使用:最简单方式(在路由中配置)
    -path('login/', obtain_jwt_token),
-自定制:(签发)
    多方式登录,手动签发token(两个方法)
      def validate(self, attrs):
      print(self.context)
      # 在这写逻辑
      username=attrs.get('username') # 用户名有三种方式
      password=attrs.get('password')
      # 通过判断,username数据不同,查询字段不一样
      # 正则匹配,如果是手机号
      if re.match('^1{9}$',username):
            user=models.User.objects.filter(mobile=username).first()
      elif re.match('^.+@.+$',username):# 邮箱
            user=models.User.objects.filter(email=username).first()
      else:
            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('用户不存在')
-自定制基于jwt的认证类(校验)
    -取出token
    -调用jwt提供的解析出payload的方法(校验是否过期,是否合法,如果合法,返回荷载信息)
    -转成user对象
    -返回
    - 自定义校验token信息
    class MyJwtAuthentication(BaseJSONWebTokenAuthentication): # 有payload字段转换成对象的操作user=self.authenticate_credentials(payload)
    class MyJwtAuthentication(BaseAuthentication): # 基于这种方法写的没有把payload转换成字典的方法
      def authenticate(self, request):
            jwt_value=request.META.get('HTTP_AUTHORIZATION')
            if jwt_value:
                try:
                #jwt提供了通过三段token,取出payload的方法,并且有校验功能
                  payload=jwt_decode_handler(jwt_value)
                except jwt.ExpiredSignature:
                  raise AuthenticationFailed('签名过期')
                except jwt.InvalidTokenError:
                  raise AuthenticationFailed('用户非法')
                except Exception as e:
                  # 所有异常都会走到这
                  raise AuthenticationFailed(str(e))
                # 因为payload就是用户信息的字典
                print(payload)
                # return payload, jwt_value
                # 需要得到user对象,
                # 第一种,去数据库查
                # 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('您没有携带认证信息')
# 19 RBAC:基于角色的权限控制(django默认的auth就是给你做了这个事),公司内部权限管理
对外的权限管理就是用三大认证
-用户表
-用户组表
-权限表
-用户对用户组中间表
-用户组对权限中间表
-用户对权限中间表
页: [1]
查看完整版本: Django核心(drf总结+代码思路)