• 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
      ' % 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类型)
  • 相关阅读:
    关于数据仓库的数据模型的思考
    西影寺的由来
    机器能像人一样思考吗?人工智能(一)机器学习和神经网络
    手机摄影超越单反?触手可及的AI让每个人成为专业摄影师!
    Flower
    Backpropagation算法 (转
    Spring Data JPA:解析CriteriaQuery
    Spring Data JPA:解析CriteriaBuilder
    Spring Data JPA:解析JpaSpecificationExecutor & Specification
    Spring Data JPA:解析SimpleJpaRepository
  • 原文地址:https://www.cnblogs.com/luocongyu/p/12120416.html
Copyright © 2020-2023  润新知