day 74

day 74 drf

01接口;接口的概念、数据接口文档、接口规范(restful)

  1. django restfarmework
  2. drf是django的插件
  3. 安装
    1. pip3 install djangorestfarmework
    2. 使用drf时要在settings中app注册

01接口

  1. 什么是接口;规定了提交请求参数的请求方式、访问器可以获取响应的反馈数据的url连接
    1. 四部分;url连接+请求方式+请求参数+响应数据
  2. 满足了同样的接口规范,无论前后端是什么语言,和数据处理方式,都可以进行交互

02文档

03restful接口规范

  1. url连接

    1. 接口都是操作前后台数据,所以需要保证数据的安全性,最好采用https协议
    2. 接口用来操作数据,与网址有区别,所以用特定的关键字表示接口,api
      1. https://api.baidu.com
      2. https://www.baidu.com/api
    3. 接口操作的数据称之为资源,在url中只体现资源名词,不体现操作资源的方式 动词
      1. 常规资源接口;
        1. https://api.baidu.com/books/
        2. https://api.baidu.com/books/(pk)/
      2. 非常规接口;和某资源不是特别密切或是不是一种资源
        1. https://api.baidu.com/login/
        2. https://api.baidu.com/place/search/
    4. 如果一个资源存在多版本结果,在url连接中要用特定符号来兼容多版本共存
      1. v1/v2;
        1. https://api.baidu.com/v1/books/
        2. https://api.baidu.com/v2/books/
    5. 群资源操作一般还有额外的限制条件,如排序、限制条数、分页等等
      1. 采用?限制条件;https://api.baidu.com/books/?ordering=-price&limit=3
  2. 请求方式

    1. 五大请求方式;get、post、put、patch、delete
      1. get;获取单个或多个资源
        1. https://api.baidu.com/books/
        2. https://api.baidu.com/books/(pk)/
      2. post;新增单个或多个资源
        1. https://api.baidu.com/books/
        2. 单增,提交单个数据字典,完成单增,返回单个结果对象
        3. 群增,提交多个数据字典,完成多增,返回多个结果对象
      3. put;整体修改单个或多个资源
        1. 整体修改多个,提供多个数据字典的数组(数据字典中要包含主键),完成修改,返回多个结果对象
        2. 整体修改单个,提供单个数据字典(主键在url中体现)完成单改,返回单个结果对象
      4. patch;局部修改单个或多个资源
        1. 方式与put请求相同吗,不同点;操作的资源如果有5个key-value键值对,put请求提供的字典必须报包含所有的键值资源,patch只需要携带需要修改的资源
      5. delete;删除单个或多个资源
        1. 多删,提供多个资源的主键数据,完成群删,
        2. 单删,不许要提供额外的数据资源,
        3. 不需要返回资源,但一般返回结果消息
  3. 响应结果

    1. 响应对象中要包含网络状态吗(网络状态信息与网络状态码是捆绑出先的)

      1. 1XX;基本信息
      2. 2XX;成功 200基本 201新增成功
      3. 3XX;重定向
      4. 4XX;客户端错误
      5. 5XX;服务端错误
    2. 数据状态码(一般是后台规定的)

      1. 0;成功
      2. 1;失败 - 1XX;具体失败信息(要在接口文档中写明)
      3. 2;无数据 - 2XX;具体无数据信息(要在接口文档中写明)
      4. 数据状态吗的信息(一般不仅仅是对数据状态吗的解释,更多是对结果的描述,给前台开发者阅读)
    3. 数据结果(常量,数组,字典),如果有子资源(图片,音频,视频),返回资源的url连接

      {
        "status": 0,
        'msg':'ok',
        'results':[{
          'name': '西游记',
          'img': 'https://api.baidu.com/media/book/xyj.png'
        },{}]
      }
      

02drf生命周期 - CBV

  1. (项目启动)APIView的as_view覆盖掉View的方法(在基础上局部禁用csrf)=> (接收到请求)调用as_view的返回值view,调用dispatch分发请求 => APIView重写了dispatch方法 => 在分发之前,对request进行了二次封装、对数据进行解析 => 三大认证 => 请求对实际响应,视图类的返回值 => 出现异常,就会交给异常处理模块进行处理 => 响应模块完成响应,渲染模块可以json或浏览器模式进行渲染

03drf的基础组件;请求、响应、渲染、解析、异常

  1. APIView 类继承了View类,重写了as_view 和 dispatch 方法
  2. 重写的as_view方法,主体还是View的as_view,只是在返回函数地址时,局部禁用了csrf认证
  3. 重写的dispatch方法,

01请求模块

  1. 将wsgi的request对象转化为drf的Requeset类的对象
  2. 封装后的request对象完全兼容wsgi的request对象,并且将原来的request保存在request._request
  3. 重新格式化请求数据存放位置
    1. 拼接参数;request.query_params
    2. 数据包参数;request.data

02解析模块

  1. 指粗粒数据包参数 - form-data,urlencoded,json
  2. 全局配置所有视图类的解析方式,配置三种
  3. 局部配置当前视图类的解析方式,配置三种

03响应模块

  1. data;响应数据
  2. status;响应的网络状态码
  3. template_name;drf完成前后台不分离,不需要了解
  4. headeers;响应头
  5. exception;一般异常响应,会将其设置为True,默认False
  6. content_type;默认就是application/json 不需要处理

04渲染模块

  1. 可以配置返回结果为json数据或页面数据
  2. postman请求结果是json,浏览器请求结果是页面

05异常模块

  1. 自定义异常处理函数;异常先交给drf处理,如果处理结果为空代表是服务端错误,再手动处理,不管哪种异常都要记录到日志文件当中

    # 导入drf的异常处理函数exceptioin_handler并重命名方便调用
    from rest_framework.views import exception_handler as drf_exception_handler
    # 对自定义的服务端错误信息进行封装
    from rest_framework.response import Response
    # 导入drf内置响应状态码
    from rest_framework import status
    # 重写异常处理函数
    def exception_handler(exc, context):
        response = drf_exception_handler(exc, context)  # 交给drf自带处理客户端错误
        # 对错误信息进行格式化封装				发生错误的视图类					错误请求的请求方式 				错误信息
        detail = '%s - %s - %s' % (context.get('view'), context.get('request').method, exc)
        if not response:  # response为空表示为,服务端错误
            response =  Response({'detail': detail}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)  # status为给客户端返回的响应状态码
        else:
            response.data = {'detail': detail}
    
        # 核心:要将response.data.get('detail')信息记录到日志文件
        # logger.waring(response.data.get('detail'))
    
        import sys  # 将异常信息以错误流的形式打印到终端
        sys.stderr.write('异常:%s\n' % response.data.get('detail'))
    
        return response		# 将处理好的响应数据返回出去
    
    
  2. 在settings中配置执行自定义异常处理函数

    # drf框架自定义配置
    REST_FRAMEWORK = {
        # 异常模块:异常处理函数
        # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',  drf自带异常处理函数
        'EXCEPTION_HANDLER': 'api.exception.exception_handler',
    }
    

04drf的序列化(核心);序列化、模型序列化、群操作序列化

  1. Serializer序列化

    1. 设置序列化字段,字段名于字段类型要与处理的model类属性名对应(值参与序列化的类型不需要设置条件)
    2. model类中有的字段,但在序列化中没有对应字段的不会参加序列化
    3. 自定义序列化方法
      1. 字段类型为SerializerMethodField(),值由 get_自定义字段名(self,model_obj)方法提供(一般自定义字段的值都与要序列化的model对象有关
    class UserSerializer(serializers.Serializer):
        username = serializers.CharField()  # model中字段
        gender = serializers.SerializerMethodField()  # 自定义字段
    
        def get_gender(self, obj):  # 自定义字段gender的值
            return obj.get_sex_display()
    
        # 注:在高级序列化与高级视图类中,drf默认帮我们处理图片等子资源
        icon = serializers.SerializerMethodField()
    
        def get_icon(self, obj):
            return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, obj.img)
    
  2. Serializer反序列化

    1. 系统校验字段与自定义校验字段定义没有区别;字段 = serializers.字段类型(条件)

       password = serializers.CharField(min_length=3, max_length=16)
      
    2. 有自定义字段时是不能直接入库的,需要设置入库规则,或将字段移除(自定义反序列化字段一般是用作全局校验用)

      re_password = serializers.CharField(min_length=3, max_length=16)
      
    3. 局部校验钩子函数validate_字段名(self,字段值value),所有字段都可以设置局部校验。规则;成功直接返回value,失败则抛出异常ValidationError('错误信息')

      		def validate_username(self, value):
              if 'g' in value.lower():
                  raise serializers.ValidationError('名字中不能有g')
              return value
      
    4. 全局校验钩子函数validate(self, 所有字段字典attrs),可以在内部进行需要多字段参与的校验。规则成功直接返回attrs,失败则抛出异常ValidationError({'异常字段': '错误信息'})

          def validate(self, attrs):
              password = attrs.get('password')
              re_password = attrs.pop('re_password')
              if password != re_password:
                  raise serializers.ValidationError({'re_password': '两次密码不一致'})
              return attrs
      
    5. 重写create方法实现增入库,返回入库成功对象

          def create(self, validated_data):
              return models.User.objects.create(**validated_data)
      
    6. 重写update方法实现改入库,返回入库成功对象

          def update(self, instance: models.User, validated_data):
              # 用户名不能被修改
              validated_data.pop('username')
              models.User.objects.filter(pk=instance.id).update(**validated_data)
              return instance
      
    class UserDeSerializer(serializers.Serializer):
        # 系统校验字段
        username = serializers.CharField(min_length=3, max_length=16, error_messages={
            'min_length': '太短',
            'max_length': '太长'
        })
        password = serializers.CharField(min_length=3, max_length=16)
    
        # 不写,不参与反序列化,写就必须参与反序列化(但可以设置required=False取消必须)
        # required=False的字段,前台不提供,走默认值,提供就一定进行校验;不写前台提不提供都采用默认值
        sex = serializers.BooleanField(required=False)
    
        # 自定义校验字段:从设置语法与系统字段没有区别,但是这些字段不能参与入库操作,需要在全局钩子中,将其取出
        re_password = serializers.CharField(min_length=3, max_length=16)
    
        # 局部钩子:
        #   方法就是 validate_校验的字段名(self, 校验的字段数据)
        #   校验规则:成功直接返回value,失败抛出校验失败信息
        def validate_username(self, value):
            if 'g' in value.lower():
                raise serializers.ValidationError('名字中不能有g')
            return value
    
        # 全局钩子:
        #   方法就是 validate(self, 所有的校验数据)
        #   校验规则:成功直接返回attrs,失败抛出校验失败信息
        def validate(self, attrs):
            password = attrs.get('password')
            re_password = attrs.pop('re_password')
            if password != re_password:
                raise serializers.ValidationError({'re_password': '两次密码不一致'})
            return attrs
    
        # 在视图类中调用序列化类的save方法完成入库,Serializer类能做的增入库走create方法,改入库走update方法
        # 但Serializer没有提供两个方法的实现体
        def create(self, validated_data):
            return models.User.objects.create(**validated_data)
    
        # instance要被修改的对象,validated_data代表校验后用来改instance的数据
        def update(self, instance: models.User, validated_data):
            # 用户名不能被修改
            validated_data.pop('username')
            models.User.objects.filter(pk=instance.id).update(**validated_data)
            return instance
    
  3. ModelSerializer类;模型序列化类 - 核心类

    1. 单表;

      1. 序列化类继承ModelSerializer,所以需要在配置类Meta中进行配置

        class UserModelSerializer(serializers.ModelSerializer):
        		class Meta:
        
      2. model配置:绑定序列化相关的model表

        model = models.User
        
      3. fields配置;采用插拔式,设置所有参与序列化与反序列化字段

        fields = ('username', 'gender', 'icon', 'password', 'sex', 're_password')
        # 所有参与序列化与反序列化的(系统或自定义)字段都要到内部进行注册
        
      4. extra_kwargs配置;

        extra_kwargs = {
          'username': {  # 系统字段不设置read_only和write_only,默认都参加
            'min_length': 3,  # 配置简单的校验信息
            'max_length': 10,
            'error_messages': {  # 配置不符合校验规则的返回信息
              'min_length': '太短',
              'max_length': '太长'
            }
          },
          'gender': {
            'read_only': True,  # 自定义的序列化字段默认就是read_only,且不能修改,可以省略
          },
          'password': {
            'write_only': True,  # 配置为只写,反序列化,用于入库但不给客户端展示的字段
          },
          'sex': {  # 像sex有默认值的字段,为选填字段('required': True可以将其变为必填字段)
            'write_only': True,
            # 'required': True
          }
        }
        
      5. 自定义反序列化字段

        re_password = serializers.CharField(min_length=3, max_length=16, write_only=True)
        # 自定义反序列化字段一般不需要返回给前端,要设置 write_only = True
        
      6. 自定义序列化字段(推荐使用)

        # 在model类中配置
            @property  # 序列化是通过反射的方法获取值
            def gender(self):
                return self.get_sex_display()
        
      7. 局部校验钩子函数validate_字段名(self,字段值value),所有字段都可以设置局部校验。规则;成功直接返回value,失败则抛出异常ValidationError('错误信息')

        		def validate_username(self, value):
                if 'g' in value.lower():
                    raise serializers.ValidationError('名字中不能有g')
                return value
        
      8. 全局校验钩子函数validate(self, 所有字段字典attrs),可以在内部进行需要多字段参与的校验。规则成功直接返回attrs,失败则抛出异常ValidationError({'异常字段': '错误信息'})

            def validate(self, attrs):
                password = attrs.get('password')
                re_password = attrs.pop('re_password')
                if password != re_password:
                    raise serializers.ValidationError({'re_password': '两次密码不一致'})
                return attrs
        
      9. ModelSerializer内部写好了create和update方法,所以不需要重写

  4. ListSerializer类;群操作序列化类 - 辅助类

    1. 重点;辅助完成单表多表群增群改操作
    2. 需要自定义辅助类继承ListSerializer
    3. 重写update方法,(ListSerializer没有写update方法,有create方法)
    class BookListSerializer(serializers.ListSerializer):
      def update(self, instance_list, validated_data_list):
        return [
          self.child.update(instance_list[index], attrs) for index, attrs in enumerate(validated_data_list)  # self.child 为自定义的序列化模型类(BookModelSerializer)
        ]
    

01单表操作

class UserV3APIView(APIView):
    # 单查群查
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
          # 获取数据
            user_obj = models.User.objects.filter(is_delete=False, pk=pk).first()
            if not user_obj:
                return Response({
                    'status': 1,
                    'msg': 'pk error',
                }, status=400)
						# 将数据交给序列化类处理
            user_ser = serializers.UserModelSerializer(user_obj, many=False)
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': user_ser.data  # 将序列化好的数据取出
            })
        else:
            user_query = models.User.objects.filter(is_delete=False).all()

            user_ser = serializers.UserModelSerializer(user_query, many=True)

            return Response({
                'status': 0,
                'msg': 'ok',
                'results': user_ser.data
            })

    # 单增
    def post(self, request, *args, **kwargs):
        user_ser = serializers.UserModelSerializer(data=request.data)
        if user_ser.is_valid():
            # 入库
            user_obj = user_ser.save()
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializers.UserModelSerializer(user_obj).data
            })
        else:
            return Response({
                'status': 1,
                'msg': user_ser.errors,
            })

02多表操作

  1. 基表;为抽象表,专门用来继承的,为表提供公有字段的,自身不会在数据库中创建

    class BaseModel(models.Model):
      class Meta:
        abstract = True
    
  2. 断关联表关系

    1. 不会影响连表查询操作
    2. 会提升增删改的效率
    3. 易于后期数据库表的重构
    4. 缺点在于,数据库本身没有连表检测,容易出现脏数据,需要通过严格的逻辑避免脏数据的产生(必要时管理脏数据)
      1. A依赖于B,先插入A,该记录对应的B记录没有产生,在没有关联的情况下该数据可以产生,该数据就是脏数据
      2. 接着再将B数据添加,脏数据就得到了处理,反过来先产生B再产生A,更符合逻辑,通过逻辑将表进行关联
      3. 连表查询,不会有任何异常
    5. related_name 在外键中,设置外键反向查询的字段名,正向找字段名,反向找related_name值
    6. on_delete在外键中必须设置,表示级联关系,在Django 1.X 下系统默认提供值为 models.CASCADE,Django 2.X 下必须手动声明,
      1. CASCADE;默认值,级联
      2. DO_NOTHING;外键不会被级联,假设A表依赖于B表,删除B表,A表的外键字段不做任何处理
      3. SET_DEFAULT;假设A表依赖于B表,B记录删除,A表的外键字段设置为default属性设置的值,必须配合default使用
      4. SET_NULL;假设A表依赖于B表,B记录删除,A表的外键字段设置为null,所以必须配合null = True使用
    7. db_constraint在外键中控制表关联,默认为True表示关联,设施False表示断开关联(该字段只能给ForeignKey)
  3. 子查询

    1. 外键字段默认显示的是外键值(int类型)
posted @ 2019-12-30 16:35  🍞面包🍞  阅读(184)  评论(0编辑  收藏  举报