zoukankan      html  css  js  c++  java
  • drf2

    复习

    """
    1、接口:url+请求参数+响应参数
    	Postman发送接口请求的工具
            method: GET
            url: https://api.map.baidu.com/place/v2/search
            params:
                ak: 6E823f587c95f0148c19993539b99295
                region: 上海
                query: 肯德基
                output: json
    	
    
    2、restful接口规范
    		https://api.baidu.com/v1/books?ordering=-price&limit=3
    		get|post|put|patch|delete
    		{
    			'status': 0,
    			'msg': 'ok',
    			'results': [
    				{
    					'title': '西游记',
    					'img': 'https://api.baidu.com/media/xyj.png'
    				}
    			]
    		}
    
    3、基于规范下原生django接口
    	api/books/					views.Book.as_view()
    	api/books/(?P<pk>.*)/		views.Book.as_view()
    	
    	class Book(View):
    		def get(self, request, *args, **kwargs):
    			pk = kwagrs.get('pk')
    			if pk:
    				# 单查
    			else:
    				# 群查
    		def post(self, request, *args, **kwargs):
    			# 增
    		def delete(self, request, *args, **kwargs):
    			# 删
    		def put(self, request, *args, **kwargs):
    			# 整体改
    		def patch(self, request, *args, **kwargs):
    			# 局部改
    			
    4、drf
    	请求的生命周期
    		APIView(as_view)禁用csrf => View(as_view)=> APIView(dispatch) => 视图类的请求方法 => 响应
    	请求模块
    		request完全兼容_request
    		重点:data/query_params
    	渲染模块
    		response数据json与browser两种渲染方式
    		重点:全局配置/局部配置
    		self.render_classes
    		自定义视图类(局部) => APIView视图类 => 自定义drf配置(全局) => drf默认配置
    """
    

    解析模块parsers

    为什么要配置解析模块:控制后台解析的数据格式。
    """
    1)drf给我们配置了多种解析数据包方式的解析类
    2)我们可以通过配置来控制前台提交的哪些格式的数据后台在解析,哪些数据不解析
    3)全局配置就是针对每一个视图类,局部配置就是针对指定的视图来,让它们可以按照配置规则选择性解析数据
    """
    
    源码入口
    # API是Application Program Interface应用程序编程接口的缩写
    # APIView类的dispatch方法中
    #initialize初始化
    request = self.initialize_request(request, *args, **kwargs)  # 点进去
    
    # 获取解析类
    # parser解析器
    parsers=self.get_parsers(),  # 点进去
    
    # 去类属性(局部配置) 或 配置文件(全局配置) 拿 parser_classes
    return [parser() for parser in self.parser_classes]
    
    全局配置:项目settings.py文件
    REST_FRAMEWORK = {
        # 全局解析类配置
        'DEFAULT_PARSER_CLASSES': [
            'rest_framework.parsers.JSONParser',  # json数据包
            'rest_framework.parsers.FormParser',  # urlencoding数据包
            'rest_framework.parsers.MultiPartParser'  # form-date数据包
        ],
    }
    
    局部配置:应用views.py的具体视图类。是否解析前台传来的对应格式数据
    # 配置了局部配置那么,postman只能发送对应的数据类型数据,否则不解析!
    
    from rest_framework.parsers import JSONParser # 局部配置第一句
    class Book(APIView):
        # 局部解析类配置,只要json类型的数据包才能被解析
        parser_classes = [JSONParser] # 局部配置第二句
        pass
    
    
    # 小复习
        def post(self, request, *args, **kwargs):
            # url拼接参数:只有一种传参方式就是拼接参数
            print(request.query_params)
            # 数据包参数:有三种传承方式,form-data、urlencoding、json
            print(request.data)
            return Response('post ok')
    

    自定义解析类

    继承BaseParser

    异常模块

    为什么要自定义异常模块:处理drf不处理的异常
    """
    1)所有经过drf的APIView视图类产生的异常,都可以提供异常处理方案
    2)drf默认提供了异常处理方案(rest_framework.views.exception_handler),但是处理范围有限
    3)drf提供的处理方案两种,处理了返回异常现象,没处理返回None(后续就是服务器抛异常给前台)
    4)自定义异常的目的就是解决drf没有处理的异常,让前台得到合理的异常信息返回,后台记录异常具体信息
    """
    
    源码分析
    # 异常模块:APIView类的dispatch方法中
    response = self.handle_exception(exc)  # 点进去
    
    # 获取处理异常的句柄(方法)
    # 一层层看源码,走的是配置文件,拿到的是rest_framework.views的exception_handler
    # 自定义:直接写exception_handler函数,在自己的配置文件配置EXCEPTION_HANDLER指向自己的
    exception_handler = self.get_exception_handler()
    
    # 异常处理的结果
    # 自定义异常就是提供exception_handler异常处理函数,处理的目的就是让response一定有值
    response = exception_handler(exc, context)
    
    如何使用:自定义exception_handler函数如何书写实现体
    # 修改自己的配置文件setting.py
    REST_FRAMEWORK = {
        # 全局配置异常模块
        'EXCEPTION_HANDLER': 'api.exception.exception_handler',
    }
    
    # api下的exception文件
    
    # 1)先将异常处理交给rest_framework.views的exception_handler去处理
    # 2)判断处理的结果(返回值)response,有值代表drf已经处理了,None代表需要自己处理
    
    # 自定义异常处理文件exception,在文件中书写exception_handler函数
    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework.views import Response
    from rest_framework import status
    def exception_handler(exc, context):
        # 先交给drf的exception_handler做基础处理
        response = drf_exception_handler(exc, context)
        """
        exc是except的缩写,异常的缩写,这是异常的根本信息。案例结果Book matching query does not exist.。
        context是异常的内容:
        之所以要传这个两个参数,是因为要明白哪个异常:哪个视图类报的哪个异常!!!
        """
        # 为空,自定义二次处理(这是我们做的处理)
        if response is None:
            return Response({
                'detail': '服务器错误'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)
        return response
    
    # 其中格式化输出,context是字典,可以这么来写!
    '%s - %s - %s' % (context['view'], context['request'].method, exc)
    # 
    

    补充:

    # 1.values
    book_dic = models.Book.objects.filter(pk=pk).values('title','price').first()
                print(book_dic)
    # 该查询结果是:
    {'title': '降牛十八掌', 'price': Decimal('67.84')}
    # 查询得到一个字典,title和price分别是字典的两个k。因此values中填写的都是字典的k!
    
    # 2.get方法查询
    book_obj = models.Book.objects.get(pk=pk)。#注意该方法查询到的结果是对象,但是该对象看起来像字符串!:《降牛十八掌》这就是打印结果,这个结果是根据models写模型类的__str__返回值方法来决定的,其实是个对象,可以使用点语法取值
    

    响应模块

    响应类构造器:rest_framework.response.Response
    比jsonresponse高级,只要是合法的json即可
    jsonresponse: "" trur {} null
    def __init__(self, data=None, status=None,
                     template_name=None, headers=None,
                     exception=False, content_type=None):
         """
            :param data: 响应数据
            :param status: http响应状态码
            :param template_name: drf也可以渲染页面,渲染的页面模板地址(不用了解)
            :param headers: 响应头
            :param exception: 是否异常了
            :param content_type: 响应的数据格式(一般不用处理,响应头中带了,且默认是json)
        """
        pass
    
    使用:常规实例化响应对象
    # status就是解释一堆 数字 网络状态码的模块
    from rest_framework import status# 就是解释一堆 数字 网络状态码的模块
    # 一般情况下只需要返回数据,status和headers都有默认值
    return Response(data={数据}, status=status.HTTP_200_OK, headers={设置的响应头})
    
    
    # 对上面补充完整的案例:
    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework.views import Response
    from rest_framework import status
    
    def exception_handler(exc, context):
        # drf的exception_handler做基础处理
        response = drf_exception_handler(exc, context)
        # 为空,自定义二次处理
        if response is None:
            return Response({
                'detail': '服务器错误'
            },status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        return response
    
    

    序列化组件:

    知识点:Serializer(偏底层)、ModelSerializer(重点)、ListModelSerializer(辅助群改)

    组件是要配合其他功能去实现的

    Serializer

    序列化准备:

    • 模型层:models.py
    class User(models.Model):
        SEX_CHOICES = [
            [0, '男'],
            [1, '女'],
        ]
        name = models.CharField(max_length=64)
        pwd = models.CharField(max_length=32)
        phone = models.CharField(max_length=11, null=True, default=None)
        sex = models.IntegerField(choices=SEX_CHOICES, default=0)
        icon = models.ImageField(upload_to='icon', default='icon/default.jpg')
    
        class Meta:
            db_table = 'old_boy_user'
            verbose_name = '用户'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return '%s' % self.name
    
    • 后台管理层:admin.py
    from django.contrib import admin
    from . import models
    
    admin.site.register(models.User)
    
    • 配置层:settings.py
    # 注册rest_framework
    # INSTALLED_APPS:installed_apps安装的应用插件
    INSTALLED_APPS = [
        # ...
        'rest_framework',
    ]
    
    # 配置数据库
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'day70',
            'USER': 'root',
            'PASSWORD': '123'
        }
    }
    
    # media资源
    # 将图片文件夹暴露出去
    MEDIA_URL = '/media/'  # 后期高级序列化类与视图类,会使用该配置
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')  # media资源路径
    
    # 国际化配置
    LANGUAGE_CODE = 'zh-hans'
    TIME_ZONE = 'Asia/Shanghai'
    USE_I18N = True
    USE_L10N = True
    USE_TZ = False
    
    • 主路由:项目下urls.py
    urlpatterns = [
        # ...
        url(r'^api/', include('api.urls')),
    
        url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
    ]
    
    • 子路由:应用下urls.py
    urlpatterns = [
        url(r'^users/$', views.User.as_view()),
        url(r'^users/(?P<pk>.*)/$', views.User.as_view()),
    ]
    

    小补充:

    # 将图片文件夹暴露出去的步骤:
    1.settings设置
    	# media资源
        # 将图片文件夹暴露出去
        MEDIA_URL = '/media/'  # 后期高级序列化类与视图类,会使用该配置
        MEDIA_ROOT = os.path.join(BASE_DIR, 'media')  # media资源路径
    2.路由设置
    	url(r'^media/(?P<path>.*)', serve, {'document_root': 				settings.MEDIA_ROOT}),
    

    序列化使用

    • 序列化层:api/serializers.py
    """
    1)设置需要返回给前台 那些model类有对应的 字段,不需要返回的就不用设置了
    2)设置方法字段,字段名可以随意,字段值由 get_字段名 提供,来完成一些需要处理在返回的数据
    """
    
    # 序列化组件 - 为每一个model类通过一套序列化工具类
    # 序列化组件的工作方式与django froms组件非常相似
    from rest_framework import serializers, exceptions
    from django.conf import settings
    
    from . import models
    
    class UserSerializer(serializers.Serializer):
        name = serializers.CharField() # 
        phone = serializers.CharField()
        # 序列化提供给前台的字段个数由后台决定,可以少提供,
        # 但是提供的数据库对应的字段,名字一定要与数据库字段相同。这是反射机制决定的
        # sex = serializers.IntegerField()  # 0和1
        # icon = serializers.ImageField()  # 字符串
    
        # 自定义序列化属性
        # 属性名随意,值由固定的命名规范方法提供:
        #       get_属性名(self, 参与序列化的model对象)
        #       返回值就是自定义序列化属性的值
        gender = serializers.SerializerMethodField()
        def get_gender(self,obj):
            # choice类型的解释型值 get_字段_display() 来访问
            return obj.get_sex_display()
    
    
        icon = serializers.SerializerMethodField()
        def get_icon(self, obj):
            # settings.MEDIA_URL: 自己配置的 /media/,给后面高级序列化与视图类准备的
            # obj.icon不能直接作为数据返回,因为内容虽然是字符串,但是类型是ImageFieldFile类型
            return '%s%s%s' % (r'http://127.0.0.1:8000', settings.MEDIA_URL, str(obj.icon))
    
    • 视图层
    """
    1)从数据库中将要序列化给前台的model对象,或是对个model对象查询出来
    	user_obj = models.User.objects.get(pk=pk) 或者
    	user_obj_list = models.User.objects.all()
    2)将对象交给序列化处理,产生序列化对象,如果序列化的是多个数据,要设置many=True
    	user_ser = serializers.UserSerializer(user_obj) 或者
    	user_ser = serializers.UserSerializer(user_obj_list, many=True)
    3)序列化 对象.data 就是可以返回给前台的序列化数据
    	return Response({
            'status': 0,
            'msg': 0,
            'results': user_ser.data
        })
    """
    
    class User(APIView):
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:
                try:
                    # 用户对象不能直接作为数据返回给前台
                    user_obj = models.User.objects.get(pk=pk)
                    # 序列化一下用户对象
                    user_ser = serializers.UserSerializer(user_obj)# 序列化组件是配合其他功能使用的,这个序列化组件是导入进来的
                    # print(user_ser, type(user_ser))
                    return Response({
                        'status': 0,
                        'msg': 0,
                        'results': user_ser.data
                    })
                except:
                    return Response({
                        'status': 2,
                        'msg': '用户不存在',
                    })
            else:
                # 用户对象列表(queryset)不能直接作为数据返回给前台
                user_obj_list = models.User.objects.all()
                # 序列化一下用户对象
                user_ser_data = serializers.UserSerializer(user_obj_list, many=True).data
                return Response({
                    'status': 0,
                    'msg': 0,
                    'results': user_ser_data
                })
    

    反序列化使用

    • 反序列层:api/serializers.py
    """
    1)设置必填与选填序列化字段,设置校验规则
    2)为需要额外校验的字段提供局部钩子函数,如果该字段不入库,且不参与全局钩子校验,可以将值取出校验
    3)为有联合关系的字段们提供全局钩子函数,如果某些字段不入库,可以将值取出校验
    4)重写create方法,完成校验通过的数据入库工作,得到新增的对象
    """
    
    class UserDeserializer(serializers.Serializer):
        # 1) 哪些自动必须反序列化
        # 2) 字段都有哪些安全校验
        # 3) 哪些字段需要额外提供校验
        # 4) 哪些字段间存在联合校验
        # 注:反序列化字段都是用来入库的,不会出现自定义方法属性,会出现可以设置校验规则的自定义属性(re_pwd)
        name = serializers.CharField(
            max_length=64,
            min_length=3,
            error_messages={
                'max_length': '太长',
                'min_length': '太短'
            }
        )
        pwd = serializers.CharField()
        phone = serializers.CharField(required=False)
        sex = serializers.IntegerField(required=False)
    
        # 自定义有校验规则的反序列化字段
        re_pwd = serializers.CharField(required=True)
    
        # 小结:
        # name,pwd,re_pwd为必填字段
        # phone,sex为选填字段
        # 五个字段都必须提供完成的校验规则
    
    
        # 局部钩子:validate_要校验的字段名(self, 当前要校验字段的值)
        # 校验规则:校验通过返回原值,校验失败,抛出异常
        def validate_name(self, value):
            if 'g' in value.lower():  # 名字中不能出现g
                raise exceptions.ValidationError('名字非法,是个鸡贼!')
            return value
    
        # 全局钩子:validate(self, 系统与局部钩子校验通过的所有数据)
        # 校验规则:校验通过返回原值,校验失败,抛出异常
        def validate(self, attrs):
            pwd = attrs.get('pwd')
            re_pwd = attrs.pop('re_pwd')
            if pwd != re_pwd:
                raise exceptions.ValidationError({'pwd&re_pwd': '两次密码不一致'})
            return attrs
    
        # 要完成新增,需要自己重写 create 方法
        def create(self, validated_data):
            # 尽量在所有校验规则完毕之后,数据可以直接入库
            return models.User.objects.create(**validated_data)
    
    • 视图层
    """
    1)book_ser = serializers.UserDeserializer(data=request_data)  # 数据必须赋值data
    2)book_ser.is_valid()  # 结果为 通过 | 不通过
    3)不通过返回 book_ser.errors 给前台,通过 book_ser.save() 得到新增的对象,再正常返回
    """
    
    class User(APIView):
        # 只考虑单增
        def post(self, request, *args, **kwargs):
            # 请求数据
            request_data = request.data
            # 数据是否合法(增加对象需要一个字典数据)
            if not isinstance(request_data, dict) or request_data == {}:
                return Response({
                    'status': 1,
                    'msg': '数据有误',
                })
            # 数据类型合法,但数据内容不一定合法,需要校验数据,校验(参与反序列化)的数据需要赋值给data
            book_ser = serializers.UserDeserializer(data=request_data)
    
            # 序列化对象调用is_valid()完成校验,校验失败的失败信息都会被存储在 序列化对象.errors
            if book_ser.is_valid():
                # 校验通过,完成新增
                book_obj = book_ser.save()
                return Response({
                    'status': 0,
                    'msg': 'ok',
                    'results': serializers.UserSerializer(book_obj).data
                })
            else:
                # 校验失败
                return Response({
                    'status': 1,
                    'msg': book_ser.errors,
                })
    

  • 相关阅读:
    2、容器初探
    3、二叉树:先序,中序,后序循环遍历详解
    Hebbian Learning Rule
    论文笔记 Weakly-Supervised Spatial Context Networks
    在Caffe添加Python layer详细步骤
    论文笔记 Learning to Compare Image Patches via Convolutional Neural Networks
    Deconvolution 反卷积理解
    论文笔记 Feature Pyramid Networks for Object Detection
    Caffe2 初识
    论文笔记 Densely Connected Convolutional Networks
  • 原文地址:https://www.cnblogs.com/ZDQ1/p/11681355.html
Copyright © 2011-2022 走看看