DRF之DRF入门规范

一 Web应用模式

  1.1 前后端不分离

    前后端不分离也就是前后端混合开发,页面会在后端渲染

 

 

 

  1.2 前后端分离

    前后端分离后,页面的渲染使用js的dom操作插入而不是使用后端语言。

    好处在于让前端人员专注前端而后端人员专注后端。

    表现形式也不仅限于网页,还包括app和小程序

 

 

 

二 API接口

  

  为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本。

  通过网络,规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介

  Web API接口和一般的url链接还是有区别的,Web API接口简单概括有下面四大特点

    • url:长得像返回数据的url链接

    • 请求方式:get、post、put、patch、delete

      • 采用get方式请求上方接口
    • 请求参数:json或xml格式的key-value类型数据

      • ak:6E823f587c95f0148c19993539b99295
      • region:上海
      • query:肯德基
      • output:json
    • 响应结果:json或xml格式的数据

      • 上方请求参数的output参数值决定了响应数据的格式

复制代码
# xml格式
https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295&region=%E4%B8%8A%E6%B5%B7&query=%E8%82%AF%E5%BE%B7%E5%9F%BA&output=xml
#json格式
https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295&region=%E4%B8%8A%E6%B5%B7&query=%E8%82%AF%E5%BE%B7%E5%9F%BA&output=json
{
    "status":0,
      "message":"ok",
    "results":[
        {
            "name":"肯德基(罗餐厅)",
            "location":{
                "lat":31.415354,
                "lng":121.357339
            },
            "address":"月罗路2380号",
            "province":"上海市",
            "city":"上海市",
            "area":"宝山区",
            "street_id":"339ed41ae1d6dc320a5cb37c",
            "telephone":"(021)56761006",
            "detail":1,
            "uid":"339ed41ae1d6dc320a5cb37c"
        }
          ...
        ]
}
复制代码

 

三 接口测试工具:Postman

  Postman是使用最多的接口调试工具,是一款可视化软件,同时支持各种操作系统平台,近期由于开始收费,也可使用免费版的postwoman。

  Postman可以直接从官网:https://www.getpostman.com/downloads/下载获得,然后进行傻瓜式安装。

  3.1 简单的get请求

 

 

 

  3.2 简单的post请求

 

 

 

四 RESTful API规范

  

 

 

 

  REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征性状态转移)。 它首次出现在2000年Roy Fielding的博士论文中。

  RESTful是一种定义Web API接口的设计风格,尤其适用于前后端分离的应用模式中。

  这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。

  事实上,我们可以使用任何一个框架都可以实现符合restful规范的API接口。

  4.1 数据的安全保障

    • url链接一般都采用https协议进行传输

      注:采用https协议,可以提高数据交互过程中的安全性

  4.2 接口特征表现

  4.3 多数据版本共存

  4.4 数据即是资源,均使用名词(可复数)

  4.5 资源操作由请求方式决定(method)

  4.6 过滤,通过在url上传参的形式传递搜索条件

  4.7 响应状态码

    4.7.1 正常响应

      • 响应状态码2xx
        • 200:常规请求
        • 201:创建成功

    4.7.2 重定向响应

      • 响应状态码3xx
        • 301:永久重定向
        • 302:暂时重定向

    4.7.3 客户端异常

      • 响应状态码4xx
        • 403:请求无权限.
        • 404:请求路径不存在
        • 405:请求方法不存在

    4.7.4 服务器异常

      • 响应状态码5xx
        • 500:服务器异常

  4.8 错误处理,应返回错误信息,error当做key

{
    error: "无权限操作"
}

  4.9 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范(仅供参考)

GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档

  4.10 需要url请求的资源需要访问资源的请求链接

复制代码
# Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么
{
      "status": 0,
      "msg": "ok",
      "results":[
        {
            "name":"肯德基(罗餐厅)",
            "img": "https://image.baidu.com/kfc/001.png"
        }
          ...
        ]
}
复制代码

  实例:

复制代码
# 响应数据要有状态码、状态信息以及数据本身
{
      "status": 0,
      "msg": "ok",
      "results":[
        {
            "name":"肯德基(罗餐厅)",
            "location":{
                "lat":31.415354,
                "lng":121.357339
            },
            "address":"月罗路2380号",
            "province":"上海市",
            "city":"上海市",
            "area":"宝山区",
            "street_id":"339ed41ae1d6dc320a5cb37c",
            "telephone":"(021)56761006",
            "detail":1,
            "uid":"339ed41ae1d6dc320a5cb37c"
        }
          ...
        ]
}
复制代码

 

五 序列化和反序列化

  api接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把数据转换格式,序列化可以分两个阶段:

  序列化: 把我们语言识别的数据转换成指定的格式提供给别人。
    字典,列表,对象------》json/xml/prop,massagepack--->提供给别人(前端或其他服务)

  反序列化:把别人提供的数据转换/还原成我们需要的格式。

  我们在django中获取到的数据默认是模型对象(qs对象),但是模型对象数据无法直接提供给前端或别的平台使用,所以我们需要把数据变成字符串或者json数据,提供给别人的过程称为序列化。

  前端传入到后台的数据---》json格式字符串---》后端存到数据库中,需要转成python中的对象---》把json格式字符串转成python对象存到数据库的过程称为反序列化。

六 DRF介绍

  核心思想: 缩减编写api接口的代码

  Django REST framework是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持。REST framework提供了一个API 的Web可视化界面来方便查看测试接口。

 

 

 

  官方文档:https://www.django-rest-framework.org/

  github: https://github.com/encode/django-rest-framework/tree/master

  

  特点:

    • 提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
    • 提供了丰富的类视图、Mixin扩展类,简化视图的编写;
    • 丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
    • 多种身份认证和权限认证方式的支持;[jwt]
    • 内置了限流系统;
    • 直观的 API web 界面;
    • 可扩展性,插件丰富

七 环境安装与配置

  当前版本DRF需要以下依赖:

    • Python (3.6, 3.7, 3.8, 3.9, 3.10)
    • Django (2.2, 3.0, 3.1, 3.2, 4.0)

  DRF是以Django扩展应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建。(若没有Django环境,需要先创建环境安装Django)

  安装:

    pip3 install djangorestframework

八 DRF快速使用

  8.1 创建一个图书类

# 创建book表
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(decimal_places=2, max_digits=5)
    author = models.CharField(max_length=32)

 

    使用pymysql数据迁移时发生报错:

 

   解决方式:

    点击错误中\backends\mysql\operations.py那行,在146行添加代码:

  8.2 创建序列化类

    在应用目录中新建serializers.py(名字可以随意)用于保存该应用的序列化类。

复制代码
from .models import Book
from rest_framework import serializers


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        # model 指明该序列化器处理的数据字段从模型类Book参考生成
        model = Book
        # fields 指明该序列化器包含模型类中的哪些字段,'__all__'指明包含所有字段
        fields = '__all__'
复制代码

  8.3 编写视图

  在应用的views.py中创建视图StudentViewSet,这是一个视图集合。

复制代码
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializer import BookSerializer


class BookView(ModelViewSet):
    # queryset 指明该视图集在查询数据时使用的查询集
    queryset = Book.objects.all()
    # serializer_class 指明该视图在进行序列化或反序列化时使用的序列化器
    serializer_class = BookSerializer
复制代码

  8.4 定义路由

复制代码
from django.contrib import admin
from django.urls import path
from rest_framework.routers import SimpleRouter
from app01 import views

router = SimpleRouter()
router.register('books', views.BookView, 'books')
urlpatterns = [
    path('admin/', admin.site.urls),
]
urlpatterns += router.urls
复制代码

 

  8.5 运行测试

    使用get请求获取数据

 

 

 

    使用post请求新增数据

 

 

 

九 CBV源码分析

复制代码
# 视图层
from django.views import View
class TextView(View):
    def get(self, request):
        print('get请求')
        return HttpResponse('get请求')

    def post(self, request):
        print('post请求')
        return HttpResponse('post请求')
# 路由层
path('text/', views.TextView.as_view()),
复制代码

 

  cbv路由写法:path('test/', views.TestView.as_view());

  path中第二个参数本应该是一个内存地址,因此可以知道views.TestView.as_view()的结果也应该是一个内存地址。

  由于我们自己定义的类中并没有as_view方法,因此明显是继承了父类View中的方法。

  查看View源码:

复制代码
@classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view
复制代码

  可见当as_view执行完就返回了一个view的内存地址,当路由匹配成功后就执行view(request)。

  同样有上面的源码可以看到,执行view返回了self.dispatch(request, *args, **kwargs),因此我们再去看dispatch的源码

  查看dispatch源码:

复制代码
 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
 def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)
复制代码

  request.method.lower表示当前请求方式的字符串小写,使用反射从当前对象(视图类的对象)找对应方法

  例如如果是get请求,handler(request)就相当于是get(request)

  如果发送的请求错误或不存在,则会报错 405 Method Not Allowed

十 drf之APIView和Request对象分析

  10.1导入路径

    在generic包的__init__文件中导入了from django.views.generic.base import View,就可以使用from django.views.generic import View来导入iew

    同理,在views包的__init__文件中导入了from django.views.generic import View,就可以使用from django.views import View来导入iew

  10.2APIView源码分析

    新建视图类OrderView:

复制代码
# 视图层
from rest_framework.views import APIView


class OrderView(APIView):
    def get(self, request):
        print('get请求')
        return HttpResponse('get请求')

    def post(self, request):
        print('post请求')
        return HttpResponse('post请求')

# 路由层
path('order/', views.OrderView.as_view()),
复制代码

 

    同样path中第二个参数本应该是一个内存地址,因此可以知道views.OrderView.as_view()的结果也应该是一个内存地址。

    由于我们自己定义的类中并没有as_view方法,因此明显是继承了父类APIView中的方法。

    查看APIVIew源码找到as_view:

复制代码
@classmethod
    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.

        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation

        view = super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)
复制代码

    返回的结果是csrf_exempt(view),csrf_exempt()的作用是去掉了csrf认证,本质还是用了View类的as_view内的view闭包函数。

    通过view = super().as_view(**initkwargs),调用APIView的父类(View)的as_view方法。

    同样的执行View中的as_view方法中的view方法返回了self.dispatch(request, *args, **kwargs),

    此时的self指的是自定义的视图类OrderView,而OrderView中没有dispatch方法,就去父类中APIView中找

    APIView中的dispatch方法:

复制代码
  def dispatch(self, request, *args, **kwargs):
          # request是新的drf提供的request,它是由老的django的request得到的
          # 通过老request,生成一个新request,drf的Request的对象
        request = self.initialize_request(request, *args, **kwargs)
        # 把新的request,放到了视图类对象中
        self.request = request
        try:
           # 执行了三大认证(认证,权限,频率)
            self.initial(request, *args, **kwargs)
            # self.http_method_names是个列表
            if request.method.lower() in self.http_method_names:
                  # 原来dispatch的核心代码
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            # 原来dispatch写的,但是request已经不是老request了,是新的
            response = handler(request, *args, **kwargs) # 执行视图函数的方法
        except Exception as exc:
              # 无论在三大认证过程中还是执行视图函数方法过程中,只要抛了异常,都会被捕获到
            # 处理全局异常
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
复制代码

    总结执行流程:

    1. 包装了新的Request对象,以后视图类中的方法中传入的request对象都是新的
    2. 在进入视图函数之前,执行了三大认证
    3. 无论三大认证还是视图函数的方法,执行过程中出了异常,都会被处理掉

    如何包装的新的request:

      查看initialize_request源码:

from rest_framework.request import Request
return Request(
            request,  # 老的request
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

      此时使用的Request方法来自rest_framework.request

      查看源码可以发现新的Request中:self._request = request,也就是将原先的request变成了self._request,使用自己的request。

      通过打印先后两个request的类型也可以发现他们是不同的

      新的:<class 'rest_framework.request.Request'>
      老的:<class 'django.core.handlers.wsgi.WSGIRequest'>

    三大认证人如何走的

      

self.initial(request, *args, **kwargs)---》APIView的
核心代码:
   self.perform_authentication(request)  # 认证
   self.check_permissions(request)   #权限
   self.check_throttles(request) # 频率

  10.3Request对象分析

复制代码
# rest_framework.request.Request ---》常用属性和方法
request.data  # 前端post请求传入的数据---》在老的request.POST-->老的只能处理urlencoded和formdata编码格式,json格式不能处理---》无论前端用什么编码post提交的数据,都从data中获取
request.files # 上传的文件对象

以后直接使用新的request.method  request.path 拿到的就是老的request.method...
对象.调用属性或方法会触发 魔法方法 __getattr__
原因在于新的request类重写了__getattr__,以后新的request.method用的时候本质就是request._request.method
  def __getattr__(self, attr):
       try:
            return getattr(self._request, attr)  # 通过反射去老的里面取
        except AttributeError:
            return self.__getattribute__(attr)

          
# 总结:新的request当老的用即可,只是多了个data前端post请求传入的数据,三种编码格式都可以
复制代码

 

posted @   临江沂水  阅读(584)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示