设为首页 收藏本站
查看: 892|回复: 0

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

[复制链接]
累计签到:504 天
连续签到:1 天
发表于 2023-2-12 12:51:58 | 显示全部楼层 |阅读模式
# 1restful规范---10
# 2django上写符合restful规范的接口
# 3drf写接口
# 4APIView--》继承了原生View--getpost
方法
    -(为什么get请求来了,就会执行get方法:原生View)
    -路由配置:视图类.as_view()--->view(闭包函数)的内存地址
    -请求来了,就会执行view(request,(路由有参数)分组分出的字段,默认传过来的字段)---self.dispatch()--》处理的

    -APIView重写了dispatch:
        包装了request对象,三大组件(权限、认证、频率)、分页、解析器、响应器、全局异常,去掉了csrf认证
# 补充HTTP版本1.02.0区别 :可以实现一次请求带多个http请求过去
# 5Request对象: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取了出来
# 6Response对象--》封装了原生的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}
        if  data:
            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):
                # instancebook对象,为什么是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、视图
    -两个视图基类 APIViewGenericAPIView(继承APIView):
        -涉及到数据库和系列化类的操作的时候,尽量用GenericAPIView
        -不涉及到数据库操作用APIView
    -5个视图拓展类(父类都是object)
        CreateModelMixincreate
        DestroyModelMixindestory
        ListModelMixin:list
        RetrieveModelMixin:retrieve
        UpdateModelMixin:update
    -9个视图子类(GenericAPIView+视图拓展类的其中几个)
        RetrieveUpdateDestroyAPIView
        CreateAPIView
        RetrieveAPIView
        DestroyAPIView
        RetrieveUpdateAPIView
        ListCreateAPIView
        UpdateAPIView
        ListAPIView
        RetrieveDestroyAPIView
    -视图集
        -ModelViewSet:5大接口都有了(内部有5个Mixin方法和GeneriticViewSet方法)
        -ReadOnlyModelViewSet:获取一条和获取多条的接口,没有增删改,只有查
       -GenericViewSetViewSetMixin+GenericAPIView        
        ViewSet:ViewSetMixin+APIView
        ViewSetMixin:类重写了as_view,
路由配置就变样了
# 10、路由
    -基本配置:跟之前一样
    -action的,必须继承ViewSetMixin
    -自动生成:DefaultSimpleRouter
        -四部曲:导入,实例化得到对象,注册多个,对象.urls(自动生成的路由)

            1from rest_framework.routers import SimpleRouter
            2router=SimpleRouter()
            3router.register('register',views.RegisterView,'register')
            4、路由相加urlpatterns+=router.urls/include:path('',include(router.urls))
        -视图类中自定义的方法,如何自动生成路由
            -在自定义的方法上加装饰器(action)
            -@action(methods=['GET','POST'],detail=True)
            -两个参数method=[GET,POST],表示这两种请求都唔那个接受
            -两个参数detail=True,表示生成带pk
的连接
# 11、三大认证(第二重要)
    -认证组件:校验用户是否登录
        写一个认证类,继承BaseAuthentication(规范必须重写authenticate方法),重写authenticate,内部写认证逻辑,认证通过返回两个值,第一个是user(可以是对象也可以是自己生成user对象但是不完整节省资源)
    认证失败,抛出异常
        -全局使用,局部使用,局部禁用(中括号空值)
        -自定义认证类
            class MyAuthentication(BaseAuthentication):
                def authenticate(self, request):
                    # 认证逻辑,如果认证通过,返回两个值
                    #如果认证失败,抛出AuthenticationFailed异常
                    token=request.GET.get('token')
                    if  token:
                        user_token=UserToken.objects.filter(token=token).first()
                        # 认证通过
                        if user_token:
                            return user_token.user,token[user_token.user这个就是为什么request.user可以拿到当前用户的对象,.user跨表到User用户对象里面去了]
                        else:
                            raise AuthenticationFailed('认证失败')
                    else:
                        raise AuthenticationFailed('请求地址中需要携带token')
------------------------------------------------------------------------------------------------------------------------
    -权限:校验用户是否有权限进行后续操作
        -写一个类继承BasePermission,重写has_permission,TrueFalse(根据request.method方法判断)
        -自定义权限类
            class UserPermission(BasePermission):
            def  has_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[ip]=[ctime,]
                        return True
                    self.history_list=self.VISIT_DIC[ip]   #当前访问者时间列表拿出来
                    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,局部使用:[MultiPartParser]
# 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[(配置成自己重写的,可以修改字段)]
-[CursorPagination]
    cursor_query_param:默认查询字段,不需要修改
    page_size:每页数目
    ordering:按什么排序,需要指定
-[LimitOffsetPagination]
    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[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()
        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、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-1003588-1-1.html 上篇帖子: 前后端分离:登录注册是实现 下篇帖子: drf实现登录和注册功能
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表