DRF(Django Rest Framework)框架基于restAPI协议规范的知识点总结

2023-12-28 13:27:56

Django Rest Framework学习

一、初识DRF组件

1.1 FBV和CBV
  • FBV:function based view

    from django.shortcuts import render, HttpResponse
    # 这种是基于FBV模式  函数模式实现Views函数
    def login(request):
        if request.method == 'GET':
            return HttpResponse("Get请求。。。。")
        return HttpResponse("POST请求。。。。")
    

    在FBV下调函数不加(),例如path(‘login/’, views.Login)url是映射,指向这块空间

  • CBV:class based view

    1.app目录下的views.py下

    from django.shortcuts import render, HttpResponse
    from django.views import View
    # 类要继承django的view模块
    class LoginView(View):
        # 这个函数是固定写法,必须叫get
        def get(self):
            return HttpResponse("get请求。。。。")
        # 这个函数是固定写法,必须叫post
        def post(self):
            return HttpResponse("post请求。。。。")
    

    2.urls.py下使用 django的view

    from django.contrib import admin
    from django.urls import path
    from userapp import views
    urlpatterns = [
        # path('admin/', admin.site.urls),
        path('login/', views.LoginView.as_view()),
    ]
    '''
       path('login/', views.LoginView.as_view()),
       相当于:
       path('login/', View.view),
       一旦浏览器发起get请求 /login/  ————>  View.view()
       def view(request, *args, **kwargs):
             
             self = cls(**initkwargs)
             return self.dispatch(request, *args, **kwargs)
       然后view()去调用dispatch()方法
       def dispatch(self, request, *args, **kwargs):
          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
                
          # 返回loginview里的get、post方法
          return handler(request, *args, **kwargs)  
    '''
    

    在CBV模式下重要的是as_view()

1.2 类、面向对象基础

运算符的应用

代码:能看懂这个就能理解操作符优先级

class B(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.foo()
    def foo(self):
        print("B foo....")
class Animal(B):
    foo = 100
    def foo(self):
        print("A foo...")

    foo = 100
scd = Animal("dfdk", 23)

反射:getattr()函数

    class Animal(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
ads = Animal("hdsdf", 23)
atrr = input(">>>查看ads的哪个属性:")
print(getattr(ads, atrr))

反射二 getattr 方法执行通过实例对象(ads)去调用Animal类的属性和方法

getattract(self(实列对象),类中的函数)

class Animal(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def foo(self):
        print("foo zhixingl")
ads = Animal("hdsdf", 23)

atrr = input(">>>查看ads的哪个属性:")
print(getattr(ads, atrr))
getattr(ads, "foo")()

1.重载(overloading method)

是在一个类里面,方法名字相同,而参数不同。返回类型呢?可以相同也可以不同。重载是让类以统一的方式处理不同类型数据的一种手段。

函数重载主要是为了解决两个问题。
1.可变参数类型。
2.可变参数个数。

另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。

好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。

那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。

好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。

2.方法重写(overiding method)

子类不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖
class LoginView(View):
    def dispatch(self, request, *args, **kwargs):
        print("dispatch....")
        # 在重写方法后,在引用父类方法需要使用super()
        super().dispatch(request, *args, **kwargs)
1.3 前后端分离模式
  • 前后端不分离模式:

    • 将数据库的数据直接嵌入模板中 实现方法是使用模板语法
    • views.py里要返回页面 使用render(),HttpResponse,redirct()
  • 前后端分离模式:

    • 使用后端的数据,queryset ,序列化(将数据库的数据传到客户端页面)、反序列化(将客户端的数据返回并保存到数据库)
1.4 API

目前市面上大多数公司开发人员使用的接口实现规范主要有:restful(基于Http协议)、RPC(基于TCP协议)性能好一些。

  • RPC (Remote Procedure Call)远程过程调用【远程服务调用】,从字面上理解就是访问/调用远程服务端提供的API接口

  • restful :面向资源开发

    符合restful 规范 urls路由:

    /books/
        get      ----- >   查看所有资源逻辑
        post     ----- >   添加逻辑
    /books/1
        get      ----- >   查看单个资源逻辑
        delete   ----- >   删除单个资源逻辑
        put      ----- >   更新单个资源逻辑
        patch    ----- >   更新资源逻辑
    
1.5 drf的使用
  • 在setting.py 的INSTALLED_APPS下添加” rest_framework“

  • 在urls路由中

    
    from django.contrib import admin
    from django.urls import path
    from userapp.views import LoginView
    from drfdemo import views
    urlpatterns = [
        # path('admin/', admin.site.urls),
        path('login/', LoginView.as_view()),
        path('student/', views.StudentView.as_view()),
    ]
    因为DRF框架的views的类视图继承的是rest_framework的APIView类
    所以 path('student/', views.StudentView.as_view()),中调用的是rest_framework的方法
    '''
    类视图继承的是rest_framework的APIView类
        def as_view(cls, **initkwargs):
        
            ########第二步调用django的View类里的as_view()函数 #######
            view = super().as_view(**initkwargs)
            view.cls = cls
            view.initkwargs = initkwargs
            
            #####第三步  在View.as_view中返回view函数(dispatch()方法)
                def view(request, *args, **kwargs):
                self = cls(**initkwargs)这个self指的是APIView
                return self.dispatch(request, *args, **kwargs)
                
            #####第四步  调用APIView的dispatch方法,因为APIView中有自己的dispatch()方法
            #####第五步  看看APIView的dispatch方法的实现
            def dispatch(self, request, *args, **kwargs):
                self.args = args
                self.kwargs = kwargs
                ##### 构建新的request对象
                request = self.initialize_request(request, *args, **kwargs)
                self.request = request
                self.headers = self.default_response_headers  # deprecate?
                
                #####  执行三个组件 权限、认证(验证有点像Django的modelForms)、限流
                self.initial(request, *args, **kwargs)
                
                #####  分发逻辑(使用getattr()反射去StudentView下找到相关的函数并执行)
                handler = getattr(
                             self, request.method.lower(), self.http_method_not_allowed
                            )
                handler = self.http_method_not_allowed
    
               # 返回loginview里的get、post方法
               return handler(request, *args, **kwargs)  
    '''
    
  • views.py文件

    from django.shortcuts import render, HttpResponse
    # from django.views import View
    # 序列化器和检验数据的功能
    from rest_framework.views import APIView
    # Create your views here.
    class StudentView(APIView):
        def get(self, request):
            # 查看get请求传回来的数据 request.GET
            print(request.GET, type(request.GET))
            print(request.query_params)
            return HttpResponse("LoginView:get请求。。。。")
    
        def post(self, request):
    
            # 查看post请求传回来的数据 使用request.data
            print(request.data, type(request.data))
            return HttpResponse("LoginView:post请求。。。。")
    
        def delete(self, request):
            return HttpResponse("LoginView:delete请求。。。。")
    

    请求参数的反序列化,

    • request.data

      返回解析之后的请求体数据,类似于Django里的request.POST和request.FILES属性:

      • 包含了解析之后的文件和非文件数据
      • 包含了对POST、PUT、PATCH请求方式解析后的数据
      • 利用了REST Framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
    • request.query_params

      与DJango标准的request.GET相同,只是更换了更正确的名称而已

    • request._request

    获取django封装的Request对象

  • 序列化

    • 创建序列化类

      from rest_framework import serializers
      # 序列化类的构建
      class StudentSerializer(serializers.Serializer):
            name = serializers.CharField()
            sex = serializers.BooleanField()
            age = serializers.IntegerField()
            class_num = serializers.CharField()
      
    • 创建序列化对象

      class StudentView(APIView):
          def get(self, request):
              # 查看get请求传回来的数据 request.GET
              print(request.GET, type(request.GET))
              print(request.query_params)
              # 获取数据库里的所有数据,序列化对象student
              student = models.Student.objects.all()
              StudentSerializer(instance=student,)
              return HttpResponse("LoginView:get请求。。。。")
      
    • Serializer的创建好序列化对象构造方法(默认many=false)

      many = True 表示序列化多个数据对象

      student = models.Student.objects.all()# 可能是多个数据对象所以使用many=True
      StudentSerializer(instance=student, data=empty,many=True )# many =True 表示多个序列化数据
      

      说明:

      1)用序列化时,将模型类对象传入instance参数

      2)用反序列化时,将要被反序列化的数据传入data参数

      3)除了instance参数、data参数外,构造的Serializer对象时还可以通过context参数额外添加数据,如

      StudentSerializer(instance=student, context={"request":request} )
      
    • 序列化器的使用

      序列化器的使用分为两个阶段

      • 处理客户端请求时,使用序列化器可以完成对数据的反序列化
      • 处理服务端响应时,使用序列化器可以完成对数据的序列化
    • 响应器

      from rest_framework.response import Response
      return Response(serializer.data)
      
    • queryset\instance 转换为JSON/xml/yaml格式

  • 反序列化(添加数据)

    • 校验

          def post(self, request):
      
              # 查看post请求传回来的数据 使用request.data
              print(request.data, type(request.data))
              serializer = StudentSerializer(data=request.data)
              # 验证方法一
              try:
                  serializer.is_valid(raise_exception=True)
                  # 将数据库插入数据库
                  models.Student.objects.create(**serializer.validated_data)
              except:
                  return Response(serializer.errors)
              # 验证方法二
              if serializer.is_valid():
                  models.Student.objects.create(**serializer.validated_data)
              return Response(serializer.errors)
      
      
1.6 由Django开发API View开发
from django.shortcuts import render
import json
from django.http import HttpResponse, JsonResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
# Create your views here.



courser_list = {
    'name': '课程',
    'price': 999.1
}

# FBV
@csrf_exempt
def course_list(request):
    if request.method == "GET":
        # return HttpResponse(json.dump(courser_list), content_type='application/json')
        return JsonResponse(courser_list)
    course = json.loads(request.body.decode('utf-8'))
    return JsonResponse(course, safe=False)

# CBV
@method_decorator(csrf_exempt, name='dispatch')
class CourseList(View):
    def get(self,request):
        return JsonResponse(courser_list)
    def post(self, request):
        course = json.loads(request.body.decode('utf-8'))
        return JsonResponse(course, safe=False)

1.7 由DRF 开发API view
  • 函数式编程(Function Based View)

    """ 一、DRF函数式编程  FBV (Function Based View)"""
    @api_view(["GET", "POST"])
    def course_list(request):
        if request.method == 'GET':
            course = Course.objects.all()
            sercourse = SerializerCourse(instance=course, many=True)  # 序列化
            return Response(data=sercourse.data, status=status.HTTP_200_OK)
        elif request.method == 'POST':
            s = SerializerCourse(data=request.data)   # 反序列化
            if s.is_valid():
                s.save(teacher=request.user)
                return Response(data=s.data, status=status.HTTP_201_CREATED)
            return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)
    
    
  • 类试图编程(Classed Based View)

    views.py
    
    from rest_framework.decorators import api_view
    from rest_framework.response import Response
    from rest_framework import status  # 状态码
    from rest_framework.views import APIView
    from drfapp import models
    from .models import Course
    from .serializers import SerializerCourse
    
    """二、类视图 CLass Based View"""
    class CourseList(APIView):
        def get(self, request):
            # 查看get请求传回来的数据 request.GET
            print(request.GET, type(request.GET))
            print(request.query_params)
            # 获取数据库里的所有数据,序列化对象student
            s = models.Course.objects.all()
            ser = SerializerCourse(instance=s, many=True)
            return Response(data=ser.data)
        def post(self,request):
            s = SerializerCourse(data=request.data)  # 反序列化
            if s.is_valid():
                s.save()
                print(type(request.data),type(s.data))
                return Response(data=s.data, status=status.HTTP_201_CREATED)
            return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)
    
    class CourseDetailList(APIView):
        @staticmethod
        def get_objects(id):
            """
            :param id: 课程id
            :return: 返回课程这个课程id的查询数据对象
            """
            try:
                return models.Course.objects.get(id=id)
            except Course.DoesNotExist:
                return
    
        def get(self, request, id):
            # 查看get请求传回来的数据 request.GET
            # 获取数据库里的所有数据,序列化对象student
            obj = self.get_objects(id=id)
            # 如果没查询的结果
            if not obj:
                return Response(data={'msg': "此课程不存在"}, status=status.HTTP_404_NOT_FOUND)
            s = SerializerCourse(instance=obj, many=False)
            print(type(request.data), type(s.data))
            return Response(data=s.data, status=status.HTTP_200_OK)
    
        def put(self, request, id):
            """
            :param request:
            :param id:
            :return:
            """
            obj = self.get_objects(id=id)
            # 如果没查询的结果
            if not obj:
                return Response(data={'msg': "此课程不存在"}, status=status.HTTP_404_NOT_FOUND)
            s = SerializerCourse(instance=obj, data=request.data)
            if s.is_valid():
                s.save()
                print(type(request.data), type(s.data))
                return Response(data=s.data, status=status.HTTP_201_CREATED)
            return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)
    
    
        def delete(self, request, id):
            obj = self.get_objects(id=id)
            # 如果没查询的结果
            if not obj:
                return Response(data={'msg': "此课程不存在"}, status=status.HTTP_404_NOT_FOUND)
            obj.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
    
    urls.py 
        
    from django.urls import path, re_path, include
    from drfapp import views
    
    urlpatterns = [
        path("cbv/list/", views.CourseList.as_view()),
        path("cbv/list/<int:id>/", views.CourseDetailList.as_view())
    ]
    
  • 通用类视图(Generic Classed Based View)

    # 通用类导包
    from rest_framework import generics
    
    """三、通用类视图 Generic CLass Based View"""
    class GCourseList(generics.ListCreateAPIView):
        queryset = models.Course.objects.all()
        serializer_class = SerializerCourse
    
    
    
    # from rest_framework import mixins
    # from rest_framework.generics import GenericAPIView
    # class GCourseDetailList((mixins.RetrieveModelMixin,
    #                                    mixins.UpdateModelMixin,
    #                                    mixins.DestroyModelMixin,
    #                                    GenericAPIView)):
    class GCourseDetailList(generics.RetrieveUpdateDestroyAPIView):
        queryset = models.Course.objects.all()
        serializer_class = SerializerCourse
        
    urls.py
    from django.urls import path, re_path, include
    from drfapp import views
    
    urlpatterns = [
        # Class Based View
        path("cbv/list/", views.CourseList.as_view()),
        path("cbv/list/<int:id>/", views.CourseDetailList.as_view()),
        # Generic Class Based View
        path("gcbv/list/", views.GCourseList.as_view()),
        
        # 注意:这里的<int:pk>要固定写成pk
        path("gcbv/list/<int:pk>/", views.GCourseDetailList.as_view())
    ]
    

put和patch的区别:

put需要写全部字段并全部更新 patch 只需要写部分需要更新的字段并添加要更改的值

  • DRF的视图集viewsets

    • 方法一使用原始创建路由 url
    from django.urls import path, re_path, include
    from drfapp import views
    
    urlpatterns = [
        # Class Based View
        path("cbv/list/", views.CourseList.as_view()),
        path("cbv/list/<int:id>/", views.CourseDetailList.as_view()),
    
        # Generic Class Based View
        path("gcbv/list/", views.GCourseList.as_view()),
        path("gcbv/list/<int:pk>/", views.GCourseDetailList.as_view()),
    
        # DRF viewsets   
        ######## 方法一:
        # get的值是:viewsets.ModelViewSet继承的ListModelMixin.list
        # post值:viewsets.ModelViewSet继承的CreateModelMixin.create
    
        # get的值是:viewsets.ModelViewSet继承的RetrieveModelMixin.retrieve
        # put值:viewsets.ModelViewSet继承的UpdateModelMixin.update
        # patch值:viewsets.ModelViewSet继承的UpdateModelMixin.partial_update
        # put值:viewsets.ModelViewSet继承的DestroyModelMixin.destroy
        path("viewsets/", views.CourseViewSet.as_view(
            {"get": "list", "post": "create"}
        )),
        path("viewsets/<int:pk>/", views.CourseViewSet.as_view(
            {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}
        ))
    ]
    
    
  • DRF的视图集viewsets 方法一使用原始创建路由 url

    • 方法二 使用DRF的routers创建路由 url
    from django.urls import path, re_path, include
    from drfapp import views
    
    # DRF viewsets
    ######## 方法二使用DRF的路由:
    from rest_framework.routers import DefaultRouter
    router = DefaultRouter()
    router.register(prefix="viewsets", viewset=views.CourseViewSet)
    
    urlpatterns = [
        path("", include(router.urls))
    ]
    
1.8 权限与认证
  • 认证: 认证 是指一个用户登录时对其身份的校验、认证; 在setting.py进行drf权限配置 (认证是最先被执行的 然后再权限检测)

     'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework.authentication.BasicAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.TokenAuthentication'
        ]
    
  • Token验证的生成方式

    • 使用Django的manage.py生成Token

      生成Token的命令
      python manage.py drf_create_token admin
      
    • 使用Django的信号机制生成Token

      第一步
      settings.文件下
      
      INSTALLED_APPS = [
      'rest_framework.authtoken',   # DRF 自带的Token认证
      ]
      第二步
      views.py文件下
      # 通过信号机制生成Token
      from django.db.models.signals import post_save
      from django.dispatch import receiver
      # django的user模型类
      from django.conf import settings
      from rest_framework.authtoken.models import Token
      
      # 通过信号机制生成token
      @receiver(post_save, sender=settings.AUTH_USER_MODEL)  #Django的信号机制
      def generate_token(sender, instance=None, created=False, **kwargs):
          """
          创建用户时自动生成token
          :param sender:
          :param instance:
          :param created:
          :param kwargs:
          :return:
          """
          if created:
              Token.objects.create(user=instance)
       第三步       
      drf_kan项目的总路由:drf_kan/urls.py
      from rest_framework.authtoken import views
      urlpatterns = [
          path('api-token-auth', views.obtain_auth_token), # 获取Token的接口
      ]
      
  • 权限:权限是指一个登录验证通过的用户能够访问哪些接口API,或者时对某个API接口能够拿到什么级别权限的数据

    'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.IsAuthenticated',
        ],
    
1.9 生成API接口文档
  • 方法一

    # 第一步
    在settings.py中
    # DRF的全局配置
    REST_FRAMEWORK = {
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',}
    #第二步
    在drf_kan项目的总路由:drf_kan/urls.py文件中
    from rest_framework.schemas import get_schema_view  # 概要  生成接口文档的包
    
    schema_view = get_schema_view(title="DRF API Document", description="xxxx")
    urlpatterns = [
        path('schema/', schema_view),
    
    ]
    
  • 方法二

    # 第一步
    在settings.py中
    # DRF的全局配置
    REST_FRAMEWORK = {
        # 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',}
    #第二步
    在drf_kan项目的总路由:drf_kan/urls.py文件中
    from rest_framework.documentation import include_docs_urls  # 生成接口文档的包
    
    urlpatterns = [
        path('docs/', include_docs_urls(title="DRF API Document", description="DRF快速入门"))
    
    ]
    

toSchema’,}
#第二步
在drf_kan项目的总路由:drf_kan/urls.py文件中
from rest_framework.schemas import get_schema_view # 概要 生成接口文档的包

schema_view = get_schema_view(title=“DRF API Document”, description=“xxxx”)
urlpatterns = [
path(‘schema/’, schema_view),

]


- 方法二

```python
# 第一步
在settings.py中
# DRF的全局配置
REST_FRAMEWORK = {
    # 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',}
#第二步
在drf_kan项目的总路由:drf_kan/urls.py文件中
from rest_framework.documentation import include_docs_urls  # 生成接口文档的包

urlpatterns = [
    path('docs/', include_docs_urls(title="DRF API Document", description="DRF快速入门"))

]

文章来源:https://blog.csdn.net/qq_42746084/article/details/135149721
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。