zoukankan      html  css  js  c++  java
  • DRF

    概念

    REST是一种软件架构设计风格,不是标准,也不是具体的技术实现,只是提供了一组设计原则和约束条件。是目前最流行的 API 设计规范,用于 Web 数据接口的设计。2000年,由Roy Fielding在他的博士论文中提出,Roy Fielding是HTTP规范的主要编写者之一。

    那么,我们所说的Django RestFramework与rest有什么关系呢?

    其实,DRF(Django RestFramework)是一套基于Django开发的、帮助我们更好的设计符合REST规范的Web应用的一个Django App,所以,本质上,它是一个Django App。

    为什么使用DRF?

    从概念就可以看出,有了这样的一个App,能够帮助我们更好的设计符合RESTful规范的Web应用,实际上,没有它,我们也能自己设计符合规范的Web应用。下面的代码演示如何手动实现符合RESTful规范的Web应用。

    class CoursesView(View):
        def get(self, request):
            courses = list()
    
            for item in Courses.objects.all():
                course = {
                    "title": item.title,
                    "price": item.price,
                    "publish_date": item.publish_date,
                    "publish_id": item.publish_id
                }
    
                courses.append(course)
    
            return HttpResponse(json.dumps(courses, ensure_ascii=False))
    

    如上代码所示,我们获取所有的课程数据,并根据REST规范,将所有资源的通过对象列表返回给用户。可见,就算没有DRF我们也能够设计出符合RESTful规范的接口甚至是整个Web App,但是,如果所有的接口都自定义,难免会出现重复代码,为了提高工作效率,我们建议使用优秀的工具。DRF就是这样一个优秀的工具,另外,它不仅仅能够帮助我们快速的设计符合REST规范的接口,还提供诸如认证、权限等等其他的强大功能。

    什么时候使用DRF?

    前面提到,REST是目前最流行的 API 设计规范,如果使用Django开发你的Web应用,那么请尽量使用DRF,如果使用的是Flask,可以使用Flask-RESTful。

    开始使用DRF
    DRF安装

    DRF官方文档中有详细介绍。使用如下命令安装,首先安装Django,然后安装DRF:

    >>> pip install django
    >>> pip install djangorestframework
    

    安装完成之后,我们就可以开始使用DRF框架来实现咱们的Web应用了,这部分内容包括如下知识点:

    • APIView
    • 解析器组件
    • 序列化组件
    • 视图组件
    • 认证组件
    • 权限组件
    • 频率控制组件
    • 分页组件
    • 响应器组件
    • url控制器

    介绍DRF,必须要介绍APIView,它是重中之重,是下面所有组件的基础,因为所有的请求都是通过它来分发的,至于它究竟是如何分发请求的呢?想要弄明白这个问题,我们就必须剖析它的源码,而想要剖析DRF APIView的源码,我们需要首先剖析django中views.View类的源码,为什么使用视图类调用as_view()之后,我们的请求就能够被不同的函数处理呢?

    Django View请求流程源码

    分析一下django中views.View类的源码。请看下图:

    DRF

    源码中最后会通过getattr在self中查找request.method.lower(),也就是get、post或者delete这些方法中的一个,那么,self是谁,就是至关重要的一点,前面讲到过,谁调用类中的方法,self就指向谁,此时,一层层往回找,我们会发现,self = cls(**initkwargs),self就是我们视图类的实例化对象,所以,dispatch函数肯定会到该视图类中找对应的方法(get或者post)。

    如果有如下函数,我想通过不修改函数内容的情况下,如何给函数新增一个统计执行时间的功能:(使用装饰器)

    def outer(func):
        def inner(*args, **kwargs):
            import time
            start_time = time.time()
            ret = func(*args, **kwargs)
            end_time = time.time()
            print("This function elapsed %s" % str(end_time - start_time))
            return ret
        return inner
    
    
    @outer
    def add(x, y):
        return x + y
    

    这是函数,如果是类呢?面向对象编程,我的程序,比如有如下代码:(利用面向对象的继承)

    class Person(object):
        def show(self):
            print("Person's show method executed!")
            
    
    class MyPerson(Person):
        def show(self):
            print("MyPerson's show method executed")
            super().show()
            
    
    mp = MyPerson()
    mp.show()
    

    接下来,我们一起来分析一下,Django RestFramework的APIView是如何对Django框架的View进行功能扩展的。

    from django.shortcuts import HttpResponse
    
    import json
    
    from .models import Courses
    
    # 引入APIView
    from rest_framework.views import APIView
    # Create your views here.
    
    
    class CoursesView(APIView):  # 继承APIView而不是原来的View
        def get(self, request):
            courses = list()
    
            for item in Courses.objects.all():
                course = {
                    "title": item.title,
                    "description": item.description
                }
    
                courses.append(course)
    
            return HttpResponse(json.dumps(courses, ensure_ascii=False))
    

    DRF APIView请求流程源码

    其他的保持不变,完整的请求流程请看下图:

    DRF

    以上就是Django RestFramework APIView的请求处理流程,我们可以通过重写dispatch()方法或者重写as_view()方法来自定制自己的想法。

    那么,Django RestFramework到底自定制了哪些内容呢?在本文的最开始,我们已经介绍过了,就是那些组件,比如解析器组件、序列化组件、权限、频率组件等。

    Ajax发送Json数据给服务器

    接下来,我们就开始学习Django RestFramework中的这些组件,首先,最基本的,就是解析器组件,在学习解析器组件之前,我需要思考思考,如何发送Json格式的数据给后端服务器?

    请看下面的代码,通过ajax请求,我们可以发送json格式的数据到后端:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
      <script src="/static/jquery-1.10.2.min.js"></script>
    </head>
    <body>
      <form action="" method="post" enctype="application/x-www-form-urlencoded">
        {% csrf_token %}
        用户名: <input type="text" name="username"/>
        密码:  <input type="password" name="password"/>
        提交:  <input type="submit" value="提交"/>
      </form>
    
      <hr>
      <button class="btn">点击发送Ajax请求</button>
    
      <script>
        $(".btn").click(function () {
          $.ajax({
            url: '',
            type: 'post',
            contentType: 'application/json',
            data: JSON.stringify({
              username: "alex",
              password: 123
            }
            ),
            success: function (data) {
              console.log(data);
            }
          })
        })
    
      </script>
    
    </body>
    </html>
    

    通过上文的知识点复习我们已经知道,Content-Type用来定义发送数据的编码协议,所以,在上面的代码中,我们指定Content-Type为application/json,即可将我们的Json数据发送到后端,那么后端如何获取呢?

    服务器对Json数据的处理方式

    按照之前的方式,我们使用request.POST, 如果打印该值,会发现是一个空对象:request post <QueryDict: {}>,该现象证明Django并不能处理请求协议为application/json编码协议的数据,我们可以去看看request源码,可以看到下面这一段:

    if self.content_type == 'multipart/form-data':
        if hasattr(self, '_body'):
            # Use already read data
            data = BytesIO(self._body)
        else:
            data = self
        try:
            self._post, self._files = self.parse_file_upload(self.META, data)
        except MultiPartParserError:
            # An error occurred while parsing POST data. Since when
            # formatting the error the request handler might access
            # self.POST, set self._post and self._file to prevent
            # attempts to parse POST data again.
            # Mark that an error occurred. This allows self.__repr__ to
            # be explicit about it instead of simply representing an
            # empty POST
            self._mark_post_parse_error()
            raise
    elif self.content_type == 'application/x-www-form-urlencoded':
        self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
    else:
        self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
    

    可见Django原生解析器并不处理application/json编码协议的数据请求。

    虽然Django的原生解析器不支持application/json编码协议,但是我们可以通过拿到原始的请求数据(request.body)来手动处理application/json请求,虽然这种方式不方便,也并不推荐,请看如下代码:

    class LoginView(View):
        def get(self, request):
            return render(request, 'classbasedview/login.html')
    
        def post(self, request):
            print(request.POST)  # <QueryDict: {}>
            print(request.body)  # b'{"username":"alex","password":123}'
            data = request.body.decode('utf-8')
            dict_data = json.loads(data)
    
            username = dict_data['username']
            password = dict_data['password']
    
            return HttpResponse(json.dumps(dict_data))
    

    通过上面的代码,我们可以通过request.body手动处理application/json请求,不过,如上文所说,并不推荐。

  • 相关阅读:
    java中几个常见的Arrays下的类
    java二分查找法的实现过程
    java冒泡排序的实现过程
    java数组中选择排序的实现
    正则表达式
    StringBuilder的使用与总结
    String的几个常用API
    关于API,注释文档以及String
    关于sqlserve2000和sqlserver2005以后版本配置连接池的一些思路
    专题-X教育
  • 原文地址:https://www.cnblogs.com/jiumo/p/10100398.html
Copyright © 2011-2022 走看看