• Django Rest-Framework


    零,DRF框架预备知识

      APIView与View的区别

        APIView继承了View

        csrf的豁免

        重现封装request对象

          原生的request赋值给了request._request

          request.query_params封装了原生的request.GET

          request.data封装了除GET外的所有信息(request.POST,request.files)

        原生的request为WSGIRequest的实例化对象

      响应对象Response

        携带HTTP表中状态码

        做模板的渲染

    一,restful规范

      REST风格

        表述状态转移:web交换方案,前后端的数据传输的设计思想

        资源的概念:在web中只要又被引用的必要都是资源

        URI

          URI:同一资源标识符

          URL:统一资源定位符,URI的子集

        统一资源接口

          根据HTTP请求方式的不同对资源进行不同的操作

          遵循HTTP请求的语义

        资源的表述

          前后端的传输叫资源的表述

          传输的不是资源的本身,而是资源的某一种表述形式

        资源的状态

          前端展示的叫资源的状态

        通过超链接的指引告诉用户接下来有哪些资源状态可以进入

        

        核心思想

          面向资源编程

             每个url就是资源的体现,不体现操作,url命名尽量用名词不要用动词

          根据HTTP请求方式的不同对资源进行不同的操作

        URL体现:

          版本

            htttps://v1.xxx.com

            https://xxx.com/v3

            体现是否为API接口

            https://v1.xxx.com/api

          过滤信息

            https://v1.xxx.com?page=1

          尽量使用HTTPS

        返回值的体现(响应请求)

          携带状态码

            1xx:信息,服务器收到请求,需要请求者继续执行操作

            2xx:成功,操作被成功接收并处理

            3xx:重定向,需要进一步的操作以完成请求

            4xx:客户端错误,请求包含语法错误或无法完成请求

            5xx:服务器错误,服务器在处理请求的过程中发生了错误

          携带错误信息

          返回值

            get 返回查看的所有或单条信息

            post 返回新增的那条数据

            put 返回更新数据

            patch 局部更新,返回更新的那条数据

            delete 返回值为空

          Hypermedia API

               如果遇到需要跳转的情况 携带跳转接口的URL

    ret = {
              code: 1000,
              data:{
              id:1,
              name:'小强',
              depart_id:http://www.baidu.com/api/v1/depart/8/
              }
    }

    二,序列化组件

      1、序列化

        实现流程

          1.如果设置了many=True

          2.把queryset当成可迭代对象去循环,得到每个模型对象

          3.把每个模型对象放入序列化器进行序列化

          4.进行字段匹配,匹配上的字段进行序列化,匹配不上的丢弃

          5.序列化的时候必须满足序列化的所有字段要求

        声明一个序列化器

    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=False)  # required=False 忽略校验
        title = serializers.CharField(max_length=32)
        pub_time = serializers.DateField()

        在视图中引用我们定义的序列化器,序列化我们的queryset数据  

    ser_obj = BookSerializer(queryset, many=True) # many=True 支持(当成)可迭代对象,循环遍历并序列化
    ser_obj = BookSerializer(models_obj) # 同样支持单个模型对象的序列化
    ser_obj.validated_data # 校验通过的数据
    return Response(ser_obj.data)

      2、反序列化

        获取前端传过来的数据

        用序列化器进行校验      

     BookSerializer(data=request.data)
    if ser_obj.is_valid():
        ser_obj.save() # 调用序列化器中的create方法操作orm创建新对象
        return Response(ser_obj.data)
    else:
        return Response

       

      3、序列化以及反序列化的时候字段类型不统一的情况

    -- required=False  # 对字段取消校验
    -- read_only=True  #  仅序列化是进行校验
    -- write_only=True  # 仅反序列化是校验

      4、ModeSerializer 帮我们实现create以及update方法

    class BookSerializer(serilalizers.ModelSerializer)
           # SerializerMethodField方法字段,会将钩子方法的返回值给字段
           text = serializers.SerializerMethodField(read_only=True)
    
            class Meta:
                model = Book # 模型类
                fields = "__all__" / ["",""]
                exclude = [""]  # 排除某些字段
                depth = 1  # 根据你的外键关系找几层,会让你所有的外键变成read_only = True
                extra_kwargs = {
                    "字段名称":{参数:值}  # 为自动生成的字段添加参数
                }
    
    # SerializerMethodField的钩子方法定义
    def get_字段名称(self,obj):
        obj 是我们循环序列化的每个模型对象
        return 自己想要的数据    

      

      4.字段的校验方法

        多个字段的校验方法,优先级为 低     

    def validate(self,attrs):
        # attrs 前端传过来的所有的数据组成的字典
        raise serializers.ValidationError("xxxx")
        return value

        单个字段的校验方法,优先级为 中

    def validate_字段名(self,value):
        # value字段的值
        raise serializers.ValidationError("xxxx")
        return value

        自定义校验方法,优先级为 高

    # 字段中添加validators指定校验方法
    title = serializers.CharField(max_length=32,validators=[my_validate])
    
    def my_validate(value):
        raise serializers.ValidationError("xxxx")
        return value

    三,视图组件

      视图的封装

    class GenericAPIView(APIView):
        query_set = None
        serializer_class = None
    
        def get_query_set(self):
            return self.query_set  # 从对象属性开始找
    
        def get_serializer(self, *args, **kwargs):  # 序列化器实例化时,以传参的方式执行
            return self.serializer_class(*args, **kwargs)
    
    
    class RetrieveModelMixin(object):
        def retrieve(self, request, pk):
            book_obj = self.get_query_set().filter(pk=pk).first()
            ser_obj = self.get_serializer(book_obj)
            return Response(ser_obj.data)
    
    
    class ListModelMixin(object):
        def list(self, request):
            # print(self.action_map) # actions:{'get': 'list', 'post': 'create'}
            ser_obj = self.get_serializer(self.get_query_set(), many=True)
            return Response(ser_obj.data)
    
    
    class CreateModelMixin(object):
        def create(self, request):
            ser_obj = self.get_serializer(data=request.data)
            if ser_obj.is_valid():
                ser_obj.save()
                return Response(ser_obj.data)
            # print(ser_obj.errors)
            return Response(ser_obj.errors)
    
    
    class UpdateModelMixin(object):
        def update(self, request, pk):
            book_obj = self.get_query_set().filter(pk=pk).first()
            ser_obj = self.get_serializer(instance=book_obj, data=request.data,partial=True) # 不用每个字段强制都要传值
            if ser_obj.is_valid():
                ser_obj.save()
                return Response(ser_obj.data)
            return Response(ser_obj.errors)
    
    
    class DestoryModelMixin(object):
        def destory(self, request, pk):
            book_obj = self.get_query_set().filter(pk=pk).first()
            if book_obj:
                book_obj.delete()
                return Response("")
            return Response("删除的对象不存在")
    
    
    class ListCreateModeMixin(GenericAPIView, ListModelMixin, CreateModelMixin): pass
    
    
    class RetrieveUpdateDestroyModelMixn(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestoryModelMixin): pass
    
    # 继承了ViewSetMixin并使用了它的as_view方法
    class ModelViewSet(ViewSetMixin, ListCreateModeMixin, RetrieveUpdateDestroyModelMixn): pass
    
    # 第一种CBV
    class BookListAPIView(ListCreateModeMixin):
        query_set = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request):
            return self.list(request)
    
        def post(self, request):
            return self.create(request)
    
    
    class BookEditAPIView(RetrieveUpdateDestroyModelMixn):
        query_set = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request, id):
            return self.retrieve(request, id)
    
        def put(self, request, id):
            return self.update(request, id)
    
        def delete(self, request, id):
            return self.destory(request, id)
    
    
    # 第二种,解耦后的CBV
    class BookModelView(ModelViewSet):
        query_set = Book.objects.all()  # 使用路由系统后,需要queryset
        serializer_class = BookSerializer
    # queryset 如果重写视图的话,使用queryset作为参数会被框架放入缓存
    # 防止数据不更新,应用调用all()方法重新获取数据
    # self.get_queryset()
    #     return self.queryset.all()
    
    
    
    
    # 第三种,直接继承框架已定义的ModelViewSet类,
    from rest_framework.viewsets import ModelViewSet
    
    class BookModelView(ModelViewSet):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
    
    class PublisherModelView(ModelViewSet):
        queryset = Publisher.objects.all()
        serializer_class = PublisherSerializer
    
    
    class AuthorModelView(ModelViewSet):
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer

      注意

    # 如果不利用ViewSetMixin的as_view方法,对self.get=self.list,如此类推赋值,而是重新的ModelViewSet的话,路由的as_view()方法需要手动穿参,重新指定请求的执行方法
    urlpatterns = [
        url(r'^list/$', views.BookModelView.as_view({'get':'list','post':'create'})), # 用新的类来处理,由于没有get,post方法,
        url(r'^list/(?P<id>d+)/$', views.BookModelView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
    ]

    四,路由组件

    # 导入
        from rest_framwork.routers import DefaultRouter
    # 实例化
        router = DefaultRouter()
    # 注册
        router.register("list",views.BookModelView)  
        # 默认生成的路由都自带参数(pk),所以视图中的方法也要带参数pk
        # actions={'get': 'list', 'post': 'create'} 路由系统自动调用as_view({'get': 'list', 'post': 'create'})
    # 把默认生成的路由注册
        urlpattrens += router.urls
    # 默认生成的路由都是带参数的!!
        # ^book/ ^list/$ [name='book-list']
        # ^book/ ^list.(?P<format>[a-z0-9]+)/?$ [name='book-list']
        # ^book/ ^list/(?P<pk>[^/.]+)/$ [name='book-detail']
        # ^book/ ^list/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$ [name='book-detail']
        # ^book/ ^$ [name='api-root']
        # ^book/ ^.(?P<format>[a-z0-9]+)/?$ [name='api-root']

    五,版本控制组件

    # 实现一个版本控制类
    from rest_framework.versioning import BaseVersioning  # 框架提供了版本控制组件
    
    class MyVersion(BaseVersioning):
        def determine_version(self,request,*args,**kwargs):
            # 拿版本号
            version = request.query_params.get("version","v1")
            return version
    
    
    # 源码:
    version,scheme = self.determine_version(request,*args,**kwargs)  # 我们配置的版本控制类一定要有determine_version方法 
        request.version,request.versioning_scheme = version,scheme  # 这个方法的返回值应该是版本号 赋值给request.version
    def determine_version(self,request,*args,**kwargs):
        if self.versioning_class is None:
            return (None,None)
            # scheme 为我们自己配置的版本控制类的实例化对象
            scheme = self.versioning_class() # scheme是我们配置的版本控制类的实例化对象 赋值给了request.versioning_scheme
            return (scheme.determine_version(request,*args,**kwargs),scheme)
    
    # 在视图
    class VersionView(APIView):
      def get(self,request,version):
        print(request._reuqest.body)
        print(request.version)
        print(request.versioning_scheme)
        if request.verison == 'v1':
          return Response('当前版本号为v1')
        elif request.version == 'v2':
          return Response('当前版本号为v2')
        return Response('当前版本不合法')

    # 需要在setting中注册
    REST_FRAMEWORK = {
      'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning' # 对应的版本控制类
    }

    # 其他几种DRF自带的版本控制类 AcceptHeaderVersioning # Accept: application/json; version=1.0 在Accept头附带版本信息 URLPathVersioning # 在URL中附带版本信息,url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'), NamespaceVersioning # url(r'^v1/', include('users.urls', namespace='v1')), 在名称空间附带版本信息 HostNameVersioning # Host: v1.example.com 在域名附带版本信息 QueryParameterVersioning # 在url参数GET /something/?version=0.1 HTTP/1.1附带版本信息

    六,认证控制组件

    from rest_framework.authentication import BaseAuthentication
    from AuthDemo.models import UserInfo
    from rest_framework.exceptions import AuthenticationFailed
    
    # 认证控制类
    class MyAuth(object):
        def authenticate(self,request):
            # 获取当前前端携带的token
            # 对对比这个token师傅合法
            token = request.query_params.get("token","")
            if not token:
                raise AuthenticationFailed("没有携带token")
            user_obj = UserInfo.objects.filter(token=token).first()
            if user_obj:
                return (user_obj,token)  # 并把返回值赋值给request.user和request.auth
            raise AuthenticationFailed("token不合法")
    
    # 在模型
    class UserInfo(models.Model):
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=32)
        token = models.UUIDField(null=True,blank=True)  # 登录成功写入token的uuid
    
    # 在视图
    class AuthView(APIView):
        authentication_classes = [MyAuth,]  # 在视图中声明authentication_classes 表示只在该视图有效
    
        def get(self, request):
            print(request.user)
            print(request.user.username)
            print(request.auth)
            return Response('登录后发送数据')
    
    # 在setting声明DEFAULT_AUTHENTICATION_CLASSES 表示全局范围应用认证
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES':'rest_framework.authentication.BaseAuthentication' # 对应的认证控制类
    }
    
    # 其他几种DRF框架自带的认证控制类
    BasicAuthentication
    SessionAuthentication
    TokenAuthentication
    RemoteUserAuthentication

    7,权限控制组件

    # 权限控制类
    from rest_framework.permissions import BasePermission
    
    class MyPermission(BasePermission):
        # 自定义的权限控制类中,必须要声明message属性和has_permission方法
        message = "错误信息" 
        def has_permission(self,request,view):   # 
            if request.user.type in [2,3]:   # 判断用户是否有权限
                return True  # 有 return True
            return False  # 没有 return False
    
    
    
    # 在视图
    class PermissionView(APIView):
        authentication_classes = [MyAuth,]
        permission_classes = [Permission,]   # 局部视图注册,只在当前视图中生效
        # 如果权限控制类返回True,程序继续执行
        # 返回False,则触发报错raise
        def get(self,request):
            # 这个接口只能vip或者vvip访问
            return Response('权限测试接口')
    
    
    
    # 在源码
        def check_permissions(self, request):
            for permission in self.get_permissions():  # 获取权限控制类的实例化对象
                if not permission.has_permission(request, self):  #  需要实现has_permission,否则走父类的方法抛出异常
                    self.permission_denied(
                        request, message=getattr(permission, 'message', None)  # 需要声明message属性,否则程序无法继续执行,报错
                    ) 
    
    
    
    # 在setting中全局注册权限组件
    REST_FRAMEWORK = {
        # 配置全局权限
        "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.BasePermission",]
    }
    
    
    
    # DRF框架中自带的权限控制类
    AllowAny
    IsAuthenticated
    IsAdminUser
    IsAuthenticatedOrReadOnly
    
    DjangoModelPermissions 的子类:
        DjangoModelPermissionsOrAnonReadOnly
        DjangoObjectPermissions

       

  • 相关阅读:
    JavaScript语言基础
    IP地址分类及CIDR划分方法
    Python静态方法实现单实例模式
    【转载】http和socket之长连接和短连接
    DDoS攻击
    Vue自定义过滤器
    解决跨域问题
    微信菜单创建
    canvas标签(1)--线条、矩形、圆形、文本、阴影、抛小球
    Bootstrap CSS概览代码文字标注篇
  • 原文地址:https://www.cnblogs.com/lianyeah/p/10129185.html
Copyright © 2020-2023  润新知