大纲


    首页 django笔记 详情
    drf

    drf

    drf rest_framework学习

    1. 下载 rest_framework

    pip install rest_framework  
    # 官方文档: https://q1mi.github.io/Django-REST-framework-documentation/api-guide/viewsets_zh/#readonlymodelviewset
    

    1.1 对应的配置

    前提条件 models.py ,views.py ,urls.py(应用模块中的 ,django项目中的) ,settings.py已经配置好了

    1.1.1 配置 models.py

    from django.db import models
    
    class BookInfo(models.Model):
        title = models.CharField(max_length=50)
        reader = models.IntegerField()
    
        def __str__(self):
            return self.title
    
        class Meta:
            db_table = "book"
    
    class HeroInfo(models.Model):
        name = models.CharField(max_length=50)
        bookid = models.ForeignKey(BookInfo,on_delete=models.DO_NOTHING,related_name='bookinfo',db_constraint=False)
    
        def __str__(self):
            return self.name
    
        class Meta:
            db_table = 'hero'
    

    1.1.2 配置 urls.py(django 项目中的)

    '''
        urls.py (django 项目同名文件夹下的)
    '''
    from django.contrib import admin
    from django.urls import path,include
    
    
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('books/', include("books.urls")),
    ]
    

    1.1.3 配置 urls.py (应用模块中的)

    '''
        urls.py (python manage.py startapp books   中的创建的 urls.py 文件夹)
    '''
    from books.views import home
    from rest_framework.routers import DefaultRouter,SimpleRouter
    
    urlpatterns = [
         path('home/',home.BookInfoViewSet.as_view()),
    ]
    
    #urlpatterns = [
        # path('home/',home.BookInfoViewSet.as_view()),
    #]
    # 默认启动drf 自带的5条路由
    # router = DefaultRouter() # 默认路由 get,post,put等增删改查路由
    # router.register(
    #     'books', home.BookInfoViewSet
    # )
    # urlpatterns += router.urls
    

    1.1.4 配置 settings.py

    '''
        settings.py 
    '''
    # 配置app 应用
    INSTALLED_APPS = [
        ···
        'rest_framework',
        "books",
        ···
    ]
    # 配置 mysql数据库
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': '数据库名字',
            'HOST': 'localhost或者127.0.0.1',
            "USER": "数据库登录账户",
            'PASSWORD': '数据库登录密码',
            "PORT": "端口号,一般都是3306",
        }
    }
    

    2. 序列化器

    序列化器模板

    '''
        serializers.py
    '''
    from rest_framework import serializers 
    from books.models import BookInfo,HeroInfo  #导入数据库模型,也就是你创建模块中的class类名
        ''' 序列化器的创建 '''
        ## 1.如果当前序列化器继承的是ModelSerializer,则需要字段转换声明
           #  字段 = serializers.字段类型(选项 = 选项值 )
    
    
        ## 2. 如果当前序列化器继承的是ModelSerializer,则需要声明调用的模型信息
        class Meta:
            model = BookInfo   # 必填
            fields = ["id","title","reader"]  # 必填 也可以是字符串和列表/元组,字符串的           值只能是"__all__"
            exclued = []    # 排除,与 fields 互斥
            read_only_fields = [] # 选填,只读字段列表,表示设置这里的字段只会在序列化阶段采用
            extra_kwargs = {  # 选填,字段额外选项声明
                  '字段':{
                      "选项":"选项值"
                  }
            }
            depth = 1 # 根据外键进行检索,数值时检索几层
    
    
        ## 3. 验证代码对象方法
         def validate_<字段名>(self,value):  # 局部数据验证
              """
              局部数据验证 方法名必须是validate_<字段名> 为名称,否则序列化器不识别
              会自动被is_valid() 方法调用
              """
             pass
             return value
    
         def validate(self,attrs):  # 全局数据验证
             # 多个字段的逻辑判断
              """
              验证来自客户端的所有数据
              类似于会员注册的密码和确认密码,就是能在validate方法中验证
              validate 是固定方法名
              参数:attrs,是在序列化器实列化时的data选项数据
              会自动被is_valid() 方法调用
              """
             return attrs
          # 还有一种验证方式 时 validators ,是在声明字段的一个选项,选项值是一个 list类型 [函数,函数...]
    
    
        ## 4. 模型操作方法
    
        def update(self, instance, validated_data): 
              """
              添加数据操作 添加数据以后,就自动实现了从字典变成模型对象的过程
              会自动被serializer.update() 或者 serializer.save()方法调用
              """
        #     '''修改数据'''  
             pass
             return instance
    
         def create(self, validated_data): 
             """
                会自动被serializer.create() 或者 serializer.save()方法调用
                更新数据操作 更新数据以后,就自动实现了从字典变成模型对象的过程     
             """
             return pass
    

    2.1 序列化器的使用

    2.1.1 导入模块和数据库模型

    在应用模块中新建 serializers.py

    '''
        serializers.py
    '''
    from rest_framework import serializers 
    from books.models import BookInfo,HeroInfo  #导入数据库模型,也就是你创建模块中的class类名
    

    2.1.2 Serializer序列化器

    2.1.2.1 序列化器

    实例化序列化器 BookInfo1Serializer(instance=data)

    • BookInfo1Serializer :定义的序列化类

    • instance : 序列化类的参数 , 默认可以不写,为 BookInfo1Serializer(data)

    • data : 需要序列化的数据,放入 instance 参数中

    serializers.py :

    # 序列化器
    class BookInfo1Serializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField()
        reader = serializers.CharField()
    

    views.py :

    from django.views import View
    from django.http.response import JsonResponse
    from rest_framework.viewsets import ModelViewSet
    
    from books.serializers import BookInfo1Serializer,BookInfo2Serializer
    from books.models import BookInfo
    
    class BookInfoViewSet(View):
        """  使用django的View  """
        def get2(self, request):
            """序列化器, 单个"""
            # 1. 获取数据源
            instance = BookInfo.objects.first()
            # 2. 获取序列化对象
            serializer = BookInfo1Serializer(instance=instance)
            # 3. 调用序列化器的data属性,获取转换之后的数据
            data = serializer.data
            # 4. 响应数据
            return JsonResponse(data,status=200)
    
        def get1(self,request):
            """序列化器,多个"""
            # 1. 获取数据源
            instance = BookInfo.objects.all()
            # 1,获取序列化器实列化对象
            serializer = BookInfo1Serializer(instanfe=instance,many=True)  #many 设置能有多条数据,会在内部调用一个循环进行遍历 QuerySet对象 获取 id
            # 3.调用序列化器对象的data属性方法获取转换后的数据
            data = serializer.data
            # 4.响应数据
            return JsonResponse(data,status=200,safe=True)  # safe 设置 对非字典对象装换成字典对象
    

    books\urls.py :

    from django.urls import path
    from books.views import home
    
    urlpatterns = [
         path('home/',home.BookInfoViewSet.as_view()),
    ]
    
    2.1.2.2 反序列化器

    serializers.py :

    # 反序列化器
    class BookInfo2Serializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(max_length=20,required=True,error_messages={"allow_null":"the is not null"},allow_null=True)
        reader = serializers.CharField(max_length=20)
    
        def validate_title(self,value):  #
            """
            局部数据验证 方法名必须是validate_<字段名> 为名称,否则序列化器不识别
            会自动被is_valid() 方法调用
            """
    
            # print(value)
            if value in ['python','django']:
                raise serializers.ValidationError("题目不能包含django,python",code='validate_title')
    
            return value
    
    
        def validate(self, attrs):
    
            # 观看python的人 等于100
            if attrs['title'] =="长江七号" and int(attrs['reader']) <= 100:
                raise serializers.ValidationError('长江七号观看的人太少了',code='validate')
            return attrs
    
        def create(self, validated_data):
            data = BookInfo.objects.create(**validated_data)
    
            return data
    

    views.py :

    from django.views import View
    from django.http.response import JsonResponse
    from rest_framework.viewsets import ModelViewSet
    
    from books.serializers import BookInfo1Serializer,BookInfo2Serializer
    from books.models import BookInfo
    
    class BookInfoViewSet(View):
            # 抛出异常
        def get(self,request):
            # 1. 接受用户的数据
            # data = json.dumps(request.body)
            # 模拟来自客户的数据
            data = {
                "title":"长江七号",
                "reader":"110",
            }
            # 1.1 调用序列化进行数据验证
            serializer = BookInfo2Serializer(data = data)
            # 1.2 调用序列化器进行数据验证
            ret = serializer.is_valid(raise_exception=True) # 抛出异常
            # 1.3 获取验证以后的结果
            return JsonResponse(dict(serializer.validated_data))
    

    books\urls.py :

    from django.urls import path
    from books.views import home
    
    urlpatterns = [
         path('home/',home.BookInfoViewSet.as_view()),
    ]
    
    2.1.2.3 新增数据入库

    serializers.py :

    def create(self, validated_data):
        data = BookInfo.objects.create(**validated_data)
    
        return data
    

    views.py :

        def get(self,request):  # 添加数据入库
            # 1. 接受用户的数据
            # data = json.dumps(request.body)
            # 模拟来自客户的数据
            data = {
                "title":"长江七号2",
                "reader":"110",
            }
            # 1.1 调用序列化进行数据验证
            serializer = BookInfo2Serializer(data=data)
            # 1.2 调用序列化器进行数据验证
            ret = serializer.is_valid(raise_exception=True) # 抛出异常
    
            # 2 获取验证以后的结果,操作数据库
            serializer.save()  # 会根据实例化序列器的说话,是否传入instance属性来自动调用create或者update方法,传入instance属性,自动调用updata方法。没有传入instance属性,自动调用create方法
    
            return JsonResponse(serializer.data,status=201)
    
    2.1.2.4 更新数据入库

    serializers.py :

    def create(self, validated_data):
        data = BookInfo.objects.create(**validated_data)
    
        return data
    

    views.py :

        # 反序列化  更新数据
        def get(self,request):
            # 1. 根据客户端访问的url地址中,获取pk值
            # books/home/2  path("/home/(?P<id>)\d+/",views.BookInfoViewSet.as_view()),
            id = 6
            try:
                book = BookInfo.objects.get(id=id)
            except Exception as e:
                return JsonResponse({"errors":"该学生不存在"},status=404)
            # 2. 模拟来自客户的数据
            data = {
                "title":"长江七号5",
                "reader":"1000",
            }
    
            # 3. 修改操作中的实例化序列器对象
            serializer = BookInfo2Serializer(instance=book,data=data)
    
            # 4. 数据验证
            serializer.is_valid(raise_exception=True)
    
            # 5. 入库
            serializer.save()
    
            # 6. 数据返回
            return JsonResponse(serializer.data)
    

    2.1.3 ModelSerializer模型序列化器

    ModelSerializer继承自 Serializer

    • 它将根据模型自动为您生成一组字段。
    • 它将自动为序列化程序生成验证程序,例如unique_together验证程序。
    • 它包括 简单默认实现。.create()``.update()

    serializers.py :

    # 模型序列化器
    class BookInfoModelSerializer(serializers.ModelSerializer):
        # 1. 声明字段
        # 字段名 = serialzers.字段类型(选项 = 选项值)
        # 也可以定义,模型里面没有的字段,需要额外声明字段
        # nickname = serializers.CharField(read_only=True)
    
        # 2. 声明模型
        class Meta:
            model = BookInfo   # 必填
            fields = ["id","title","reader"]  # 必填 也可以是字符串和列表/元组,字符串的值只能是"__all__"
            # exclued = []  # 排除,与 fields 互斥
            # read_only_fields = [] # 选填,只读字段列表,表示设置这里的字段只会在序列化阶段采用
            # extra_kwargs = {  # 选填,字段额外选项声明
            #       '字段':{
            #           "选项":"选项值"
            #       }
            # }
            extra_kwargs = {  # 选填,字段额外选项声明
                'reader': {
                    "required": False,
                    "error_messages": {
                        "required": "reader字段不能为空"
                    }
                  },
    
                }
    
        # 3. 验证代码的对象方法
        # def create(self, validated_data):
            # 密码加密
            # validated_data["password"] = make_password(validated_data["password"])
            # super().create(validated_data)
    
        # 4. 模型操作方法
    

    books\urls.py

    urlpatterns = [
         ...
         path('homeModel/', home.BookInfoViewSet1.as_view()), # 模型序列化器视图API
    
    ]
    

    views.py:

    # 模型化序列化器 Api
    class BookInfoViewSet1(View):
    
    
        """  使用django的View  """
        """序列化器,单个"""
        def get1(self, request):
            # 1. 获取数据源
            instance = BookInfo.objects.first()
            # 2. 获取序列化对象
            serializer = BookInfoModelSerializer(instance=instance)
            # 3. 调用序列化器的data属性,获取转换之后的数据
            data = serializer.data
            # 4. 响应数据
            return JsonResponse(data,status=200)
    
        """序列化器,多个"""
        def get2(self,request):
            """序列化器,多个"""
            # 1. 获取数据源
            instance = BookInfo.objects.all()
            # 1,获取序列化器实列化对象
            serializer = BookInfoModelSerializer(instance,many=True)  #many 设置能有多条数据,会在内部调用一个循环进行遍历 QuerySet对象 获取 id
            # 3.调用序列化器对象的data属性方法获取转换后的数据
            data = serializer.data
            # 4.响应数据
            return JsonResponse(data,status=200,safe=False,json_dumps_params={'ensure_ascii':False})  # safe 设置 对非字典对象装换成字典对象
    
        # 反序列化
        def get3(self,request):
            # 1. 接受用户的数据
            # data = json.dumps(request.body)
            # 模拟来自客户的数据
            data = {
                "title":"django",
                "reader":"",
            }
            # 1.1 调用序列化进行数据验证
            serializer = BookInfoModelSerializer(data = data)
            # 1.2 调用序列化器进行数据验证
            ret = serializer.is_valid() # 不抛出异常
            # 1.3 获取验证以后的结果
            if ret:
                return JsonResponse(dict(serializer.validated_data),json_dumps_params={'ensure_ascii':False})
            else:
                return JsonResponse(dict(serializer.errors),json_dumps_params={'ensure_ascii':False})
            # 2. 操作数据库
    
        # 抛出异常
        def get4(self,request):
            # 1. 接受用户的数据
            # data = json.dumps(request.body)
            # 模拟来自客户的数据
            data = {
                "title":"长江七号",
                "reader":"",
            }
            # 1.1 调用序列化进行数据验证
            serializer = BookInfoModelSerializer(data = data)
            # 1.2 调用序列化器进行数据验证
            ret = serializer.is_valid(raise_exception=True) # 抛出异常
            # 1.3 获取验证以后的结果
            return JsonResponse(dict(serializer.validated_data))
            # 2. 操作数据库
    
        # 反序列化  添加数据
        def get5(self,request):
            # 1. 接受用户的数据
            # data = json.dumps(request.body)
            # 模拟来自客户的数据
            data = {
                "title":"长江七号2",
                "reader":"110",
            }
            # 1.1 调用序列化进行数据验证
            serializer = BookInfoModelSerializer(data=data)
            # 1.2 调用序列化器进行数据验证
            ret = serializer.is_valid(raise_exception=True) # 抛出异常
    
            # 2 获取验证以后的结果,操作数据库
            serializer.save()
    
            return JsonResponse(serializer.data,status=201)
    
        # 反序列化  更新数据
        def get6(self,request):
            # 1. 根据客户端访问的url地址中,获取pk值
            # books/home/2  path("/home/(?P<id>)\d+/",views.BookInfoViewSet.as_view()),
            id = 6
            try:
                book = BookInfo.objects.get(id=id)
            except Exception as e:
                return JsonResponse({"errors":"该学生不存在"},status=404)
            # 2. 模拟来自客户的数据
            data = {
                "title":"长江七号71",
                "reader":"1000",
            }
    
            # 3. 修改操作中的实例化序列器对象
            serializer = BookInfoModelSerializer(instance=book,data=data)
    
            # 4. 数据验证
            serializer.is_valid(raise_exception=True)
    
            # 5. 入库
            serializer.save()
    
            # 6. 数据返回
            return JsonResponse(serializer.data)
    

    2.2 附加说明

    1) 在序列化器进行save() 保存时,可以额外传递数据,这些数据再create()update()中的validated_data参数获取到

    # request.user 是django中记录当前登录用户的模型对象
    serializer.save(owner = request.user) # 可以再save中,传递一些不需要验证的数据到模型里面
    

    2)默认序列化器必须传递所有required的字段,否则会抛出验证异常但是我们可以使用partial 参数来允许部分字段更新

    # Update "comment" with partial data
    serializer = CommentSerializer(comment,data={'content':'foo bar'},partial=True)
    

    2.3 Serializer 和 ModelSerializer

    数据来源与mysql也就是模型的时候使用 Modelserializer,否则就用serializer

    3. HTTP 请求响应

    drf除了在数据序列化部分见写代码以外,还在视图中提供了简写操作。所以django原有的django.views.View类基础上,drf封装了多个视图子类出来提供给我们使用。

    Django REST framework 提供的视图主要作用:

    • 控制序列化器的执行(检验,保存,转换数据)
    • 控制数据库查询的执行
    • 调用请求类和响应类【这两个类也是由drf帮我们再次扩展了一些功能】

    内容协商:drf在django原有的基础上,新增一个request对象集成到了APIView视图类并在django原有的HttpResponse响应类的基础上实现了一个子类rest_framework.response.Response响应类。这两个类,都是基于内容协商来完成数据的格式转换。

    ​ 换句话说:内容协商在客户端和服务端之间的数据通信的过程中,基于协议郑家数据格式的声明,方便对端 理解本端发送的数据格式和期望放回的数据格式。

    img

    创建一个子应用req

    python manange.py startapp req
    

    在django项目中文件夹下的urls.py

    urlpatterns = [
        path('admin/', admin.site.urls),
        path('books/', include("books.urls")),  # 序列化器的学习
        path("req/",include('req.urls')),       # Http请求响应的学习
    ]
    
    • request->parser解析类->识别客户端请求头中的Content-Type来完成数据转换成->类字典(QueryDict,字典的子类)

    • response->renderer渲染类->识别客户端请求头的“Accept”来提取客户端期望的返回数据格式->转换成客户端的期望格式数据

    注册子应用

    settings.py

    INSTALLED_APPS = [
        ...
        'rest_framework',
        "books",  # 序列化器的学习
        "req",    # Http请求响应
    
    ]
    

    req\views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    
    class BookInfoAPIView(APIView):
        def get(self,request):
            print(f"drf.request={request}")  
            # rest_framework.request.Request 是属于drf单独声明的请求处理对象,与django提供的HttpRequest不是同一个,甚至没有任何继承关系
            print(f"django.request={request._request}")  # 拿到django 的request
            return Response({'msg': "ok"})
    

    在创建子应用urls.py

    req\urls.py

    from django.urls import path
    from req import views
    
    
    urlpatterns = [
        path("book/",views.BookInfoAPIView.as_view())
    ]
    

    3.1 常用属性

    3.1.1 request

    1) .data

    request.data返回解析之后的请求体数据。类似于django中标准的request.POSTrequest.FILES属性 ,但是提供如下特性:

    • 包含了解析之后的文件和非文件数据
    • 包含了对POSTPUTPATCH请求方式解析后的数据
    • 利用了REST frameworkparser解析器,不仅支持表单类型数据,也支持json数据
    2).query_params

    查询参数,也叫查询字符串。

    request.query_params 与django标准request.GET 相同,只是更换了正确的名字而已。

    3)request._request

    获取django封装的Request对象

    基本使用

    使用Postman进行接口传参

    views.py

        def get(self, request):
            print(f"drf.request={request}")  # rest_framework.request.Request 是属于drf单独声明的请求处理对象,与django提供的HttpRequest不是同一个,甚至没有任何继承关系
            print(f"django.request={request._request}")  # 拿到django 的request
            return Response({'msg': "ok"})
    
        def post(self, request):
            from rest_framework import status
            # 添加数据
            """获取请求体数据"""
            # 获取表单提交的数据
            print(f"request.data={request.data}")  # 接受的数据已经被Parse解析器转换成字典数据了
            # 用户传输的json数据  request.data=<QueryDict: {'name': ['1310832212'], 'password': ['123456'], 'Type': ['1']}>
            # 获取地址连提交的数据  也就是get 提交的数据
    
            print(f"request.query_params{request.query_params}")
            # 地址栏数据  request.query_params<QueryDict: {'mat': ['123']}>
            return Response({'msg': "ok"},status=status.HTTP_201_CREATED
    

    3.1.2 Response

    drf 的 response 时django HttpResponse 的子类

    REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据会被转移(renderer渲染器)成符合前端需求的类型。

    REST framework提供了Renderer渲染器,用来根据请求头中的Accept(接受数据类型声明)来自懂转换响应数据到对应格式。如果前端请求中未声明Accept,则会采用Content-Type方式处理响应数据,我们可以通过配置来修改默认响应格式。

    可以在rest_framework.settings查看所有的drf默认配置项

    """
        ctrl+鼠标左键  点击 drf的 Response 进入 rest_framework.settings
    """
    DEFAULTS = {
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',  # json数据格式,无界面
            'rest_framework.renderers.BrowsableAPIRenderer',  # 默认响应的api界面
        ],
        ...
    }
    
    1)response构造方法
    Response(data,status=None,template_name=None,headers=None,content_type=None)
    """
        data:响应序列化处理后的数据
        status:响应的状态码,如200,201... 可导入 from rest_framework import status 状态码常量文件
        template_name:响应的模板,只是对默认的api模板进行改造
        headers:自定应响应请求头
        content_type:规定parser解析器解析的数据格式,如conten_type:"json" 解析成的数据就是json
    """
    
    2)response对象属性

    工作中很少用

    (1).data

    ​ 获取传给response对象的序列化后,但尚未render处理的数据

    (2).status_code

    ​ 获取状态码的数字

    (3).content

    ​ 获取经过render处理后的响应数据

    4. 视图View

    Django REST framework 提供的视图主要作用

    • 控制序列化的执行(检验,保存,转换数据)
    • 控制数据模型的操作

    4.1 普通视图

    4.1.1 2 个视图基类

    1)APIView基本视图
    rest_framework.views.APIView
    

    APIView类在以下方面与常规类不同:View

    • 传递给处理程序方法的请求将是REST框架的实例,而不是Django的实例。Request``HttpRequest
    • 处理程序方法可以返回 REST 框架的response对象 ,而不是 Django 的 。视图会响应数据设置(renderer)符合前端要求的格式
    • 任何异常情况都将被捕获并调解为适当的响应。APIException
    • django的view中所有异常都是以HTML格式显示
    • drf的APIView或者APIView的子类会自动根据客户端的Accept进行错误信息的格式转换

    • 重新声明一个新的as_view方法并在dispatch()进行路由分发前,会对请求的客户端进行身份认证,权限检查,流量控制。

    APIView除了继承了View原有的属性方法意外,害新增了类属性:

    • authentication_classes 列表或元组,身份认证类
    • permissoin_classes 列表或元组,权限检查类
    • throttle_classes 列表或元组,流量控制类

    APIView中仍以常规的类视图方法来实现get()、post()或者其他请求方式的方法。

    创建子应用模块

    python manage.py startapp dome_views
    

    注册子应用模块settings.py

    INSTALLED_APPS = [
        ...
        'rest_framework',
        "books",  # 序列化器的学习
        "req",    # Http请求响应
    
    ]
    

    创建序列化器 dome_views\serializers.py

    from rest_framework import serializers
    from books.models import BookInfo
    
    class BookInfoSerializers(serializers.ModelSerializer):
        class Meta:
            model = BookInfo
            fields = "__all__"
    

    django同名文件夹下 urls.py:

    urlpatterns = [
        ...
        path("dome_views/",include('dome_views.urls'))  # 视图学习
    ]
    

    dome_views\views.py

    from rest_framework import status
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from books.models import BookInfo
    from .serializers import BookInfoSerializers
    
    """
    get     获取所有数据
    post    新增一条数据
    get     查看详情
    put     修改数据
    delete  删除数据
    """
    class BookViewSerializer(APIView):
        def get(self,request):
            # 1. 获取数据
            data = BookInfo.objects.all()
            # 2. 实例化序列化器
            serializer = BookInfoSerializers(data,many=True)
            # 3. 返回数据
            return Response(serializer.data)
    
        def post(self,request):
            # 1. 获取用户提交的数据
            data = request.data
            # 2. 实例化序列化器
            serializer = BookInfoSerializers(data = data)
            # 3. 验证数据,保存数据
            serializer.is_valid(raise_exception=True)
            serializer.save()
            # 4. 返回数据
            return Response({"msg":"添加成功"},status=status.HTTP_201_CREATED)
    
    
    class BookInfoViewserializer(APIView):
        def get(self,request,id):
    
            # 1. 根据id获取模型对象数据
            try:
                book = BookInfo.objects.get(id=id)
            except BookInfo.DoesNotExist:
                return Response(status=status.HTTP_404_NOT_FOUND)
            # 2. 实例化序列器
            serializer = BookInfoSerializers(book)
            # 3. 返回数据
            return Response(serializer.data)
    
        def put(self,request,id):
            # 1. 根据id 获取模型对象
            try:
                book = BookInfo.objects.get(id=id)
            except BookInfo.DoesNotExist:
                return Response(status=status.HTTP_404_NOT_FOUND)
            # 2. 获取用户提交的数据
            data = request.data
    
            # 3. 实例化序列化器
            serializer = BookInfoSerializers(instance=book,data=data)
    
            # 4. 验证数据,保存数据
            serializer.is_valid(raise_exception=True)
            serializer.save()
    
            # 5. 返回数据
            return Response(serializer.data,status=status.HTTP_201_CREATED)
    
        def delete(self,request,id):
            # 1. 根据id 获取模型对象
            try:
                BookInfo.objects.get(id=id).delete()
            except BookInfo.DoesNotExist:
                pass
            return Response({"msg":"删除成功"},status=status.HTTP_204_NO_CONTENT)
    

    dome_views\urls.py

    from django.urls import re_path
    from rest_framework.urls import path
    from . import views
    
    urlpatterns = [
        path("book/",views.BookViewSerializer.as_view()),
        re_path("^bookinfo/(?P<id>\d+)/$",views.BookInfoViewserializer.as_view())
    ]
    
    2)GenericAPIView[通用视图类]

    通用视图类主要作用就是把视图中的独特的代码抽取出来,让视图方法中的代码更加的通用,方便把通用代码进行简写

    rest_framework.generice.GenericAPIView
    

    继承自APIView,主要增加了操作序列化器和数据库查询的方法,作用时为下面Mixin扩展类的执行提供方法支持。通常在使用时,可以搭配一个或多个Mixin扩展类。

    提供的关于序列化器使用的属性与方法

    • 属性

    • serializer_class 指明视图使用的序列化器

    • 方法

    • get_serializer_class(self)

      当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了。

    返回序列化器,默认返回serializer_class,可以重写,例如:

    例子:

    ```python class BookInfo2ViewSerializer(GenericAPIView): queryset = BookInfo.objects.all() # 指定数据集

    # 一个视图类中,单个序列化器
      # serializer_class = BookInfoSerializers
    
      # 一个视图类中,多个序列化器
    def get_serializer_class(self):
          # 如果是 put 请求 返回 BookInfoSerializer1 ,否则 返回 BookInfoSerializer2
          if self.request.mothed.lower() == "put": 
              return BookInfoSerializer1
          else:
              return BookInfoSerializer2
    

    ``` - get_serializer(self,args,*kwargs)

    返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法。
    
    注意:该方法在提供序列化器对象的时候,会向序列化器独享的context属性补充三个数据:**request**、**format**、**view**、这三个数据对象可以在定义序列化器时使用。
    
    - **request**  当前视图的请求对象
    - **view **       当前请求的类试图对象
    - **format **   当前i请求期望返回的数据格式
    

    提供的关于数据库查询的属性与方法

    • 属性

    • queryset 指明使用的数据查询集

    • 方法

    • get_queryset(self)

      返回视图使用的查询集,主要用来提供Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:

      python from rest_framework.generics import GenericAPIView class Book2ViewSerializer(GenericAPIView): queryset = BookInfo.objects.all() # 指明使用的数据查询集 serializer_class = BookInfoSerializers def get(self,request): # 1. 获取模型所有数据 books = self.get_queryset() ######## get_queryset(self) # 2. 实列化序列器 serializer = self.get_serializer(books,many=True) # 3. 返回结果 return Response(serializer.data)

    • get_object(self)

      返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用。在试图中可以调用该方法获取详情信息的模型类对象。

      若详情访问的模型类对象不存在,会返回404。

      该方法会默认使用APIView提供的check_object permissions方法检查当前对象是否有权限被访问。举例:

      当参数为pk的时候才能使用get_object()方法 , 因为参数pk已经是在源码里面内置了,所以只能是pk

      注意:urls.py : 获取的参数也必须是pk,不让会报错,大概就是路由参数和视图类的参数不同,例如:

      ​ re_path("^bookinfo2/(?P\d+)/$",views.BookInfo2ViewSerializer.as_view())

      ```python class BookInfo2ViewSerializer(GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializers """查询一个数据详情""" def get(self,request,pk): # 1. 通过id获取模型数据

          # instance = self.get_queryset().get(id=pk)  
          # self.get_queryset().get(id=pk) === instance = self.get_object()
          instance = self.get_object()  
          # 当参数为pk的时候才能使用get_object()方法 , 因为参数pk已经是在源码里面内置了,所以只能是pk
      
          # 2. 实例化序列化器
          serializer = self.get_serializer(instance)
      
          # 3. 返回数据
          return Response(serializer.data)
      

      ```

    其他可以设置的属性

    • pagination_class指明分页控制类
    • filter_backends指明数据过滤控制后端

    实列:

    dome_views/urls.py:

    from django.urls import re_path
    from rest_framework.urls import path
    from . import views
    
    urlpatterns = [
        # 基本是图类 APIView
        ....
    
        # 通用视图类 GenericAPIView
        path('book2/',views.Book2ViewSerializer.as_view()),
        re_path("^bookinfo2/(?P<pk>\d+)/$",views.BookInfo2ViewSerializer.as_view())
    ]
    

    dome_views/views.py:

    """ 通用视图类 GenericAPIView"""
    from rest_framework.generics import GenericAPIView
    class Book2ViewSerializer(GenericAPIView):
        queryset = BookInfo.objects.all()  # 指明使用的数据查询集
        serializer_class = BookInfoSerializers
        def get(self,request):
            # 1. 获取模型所有数据
            books = self.get_queryset()
            # 2. 实列化序列器
            serializer = self.get_serializer(books,many=True)
            # 3. 返回结果
            return Response(serializer.data)
    
        def post(self,request):
            # 1. 获取用户提交的数据
            data = request.data
    
            # 2. 实例化序列化器
            serializer = self.get_serializer(data=data)
    
            # 3. 验证数据,保存数据
            serializer.is_valid(raise_exception=True)
            serializer.save()
    
            # 4. 返回数据
            return Response(serializer.data,status=status.HTTP_201_CREATED)
    
    
    class BookInfo2ViewSerializer(GenericAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
        """查询一个数据"""
        def get(self,request,pk):
            # 1. 通过id获取模型数据
    
            # instance = self.get_queryset().get(id=pk)  # self.get_queryset().get(id=pk) === instance = self.get_object()
            instance = self.get_object()  # 当参数为pk的时候才能使用get_object()方法 , 因为参数pk已经是在源码里面内置了,所以只能是pk
    
            # 2. 实例化序列化器
            serializer = self.get_serializer(instance)
    
            # 3. 返回数据
            return Response(serializer.data)
    
        def put(self,request,pk):
            # 1. 获取用户输入的数据
            data = request.data
            # 2. 通过id获取模型数据
            # instance = self.get_queryset().get(id=pk)
            instance = self.get_object()
            # 3. 实例化序列化器
            serializer = self.get_serializer(instance=instance ,data=data)
            # 4. 验证数据,保存数据
            serializer.is_valid(raise_exception=True)
            serializer.save()
            # 5. 返回结果
            return Response(serializer.data,status=status.HTTP_201_CREATED)
    
        def delete(self,request,pk):
            # 1. 通过id 删除数据
            # self.get_queryset().get(id=id).delete()
            instance = self.get_object()
            # 2. 返回结果
            return Response(status=status.HTTP_204_NO_CONTENT)
    

    4.1.2 5 个视图扩展类

    也叫混入类。

    作用: 提供了几种后端视图(对数据资源进行增删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。

    这五个扩展类需要搭配GenericAPIMView通用视图基类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。

    1)ListModelMixin

    提供一个 .list(request, *args, **kwargs) 方法,实现列出结果集。

    如果查询集被填充了数据,则返回 200 OK 响应,将查询集的序列化表示作为响应的主体。相应数据可以任意分页。

    实例:

    from rest_framework.mixins import ListModelMixin
    class BookMixinSerializer(GenericAPIView,ListModelMixin):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    
        """ 查询所有数据 """
        def get(self,request):
            return self.list(request)  # 可以进行查看源代码
    
    2)CreateModelMixin

    提供 .create(request, *args, **kwargs) 方法,实现创建和保存一个新的model实例

    如果创建了一个对象,这将返回一个 201 Created 响应,将该对象的序列化表示作为响应的主体。如果序列化的表示中包含名为 url的键,则响应的 Location 头将填充该值。

    如果为创建对象提供的请求数据无效,将返回 400 Bad Request,其中错误详细信息作为响应的正文。

    from rest_framework.mixins import CreateModelMixin
    class BookMixinSerializer(GenericAPIView,CreateModelMixin):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    
        """ 添加一个数据 """
        def post(self,request):
            return self.create(request)  # 可以进行查看源代码
    
    3)RetrieveModelMixin

    提供一个 .retrieve(request, *args, **kwargs) 方法,实现返回响应中现有模型的实例

    如果可以检索对象,则返回 200 OK 响应,将该对象的序列化表示作为响应的主体。否则将返回 404 Not Found

    from rest_framework.mixins import RetrieveModelMixin
    class BookInfoMixinSerializer(GenericAPIView,RetrieveModelMixin):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    
        """ 查看详情 """
        def get(self,request,pk):
            return self.retrieve(request,pk)
    
    4)UpdateModelMixin

    提供 .update(request, *args, **kwargs) 方法,实现更新和保存现有模型实例

    同时还提供了一个 .partial_update(request, *args, **kwargs) 方法,这个方法和 update 方法类似,但更新的所有字段都是可选的。这允许支持 HTTP PATCH 请求。

    如果一个对象被更新,这将返回一个 200 OK 响应,将对象的序列化表示作为响应的主体。

    如果为更新对象提供的请求数据无效,将返回一个 400 Bad Request 响应,错误详细信息作为响应的正文。

    from rest_framework.mixins import UpdateModelMixin
    class BookInfoMixinSerializer(GenericAPIView,UpdateModelMixin):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    
        """ 更新数据 """
        def put(self,request,pk):
            return self.update(request,pk)
    
    5)DestroyModelMixin

    提供一个 .destroy(request, *args, **kwargs) 方法,实现删除现有模型实例

    如果删除对象,则返回 204 No Content 响应,否则返回 404 Not Found

    from rest_framework.mixins import DestroyModelMixin
    class BookInfoMixinSerializer(GenericAPIView,DestroyModelMixin):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    
        """ 删除数据 """
        def delete(self,request,pk):
            return self.destroy(request,pk)
    

    完整代码:views.py

    """ 
    
    使用drf内置的模型扩展类[混入类]结合GenericAPIView实现通用视图方法的简写操作
    from rest_framework.mixins import ListModelMixin获取多条数据,返回响应结果       list
    from rest_framework.mixins import CreateModelMixin添加一条数据,返回响应结果     create
    from rest_framework.mixins import RetrieveModelMixin 获取一条数据,返回响应结果  retrieve
    from rest_framework.mixins import UpdateModelMixin更新一条数据,返回响应结果     update(更新全部字段)和partial_update(更新单个或部分字段,例如修改密码,修改头像)
    from rest framework.mixins import DestroyModelMixin 删除一条数据,返回响应结果   destroy
    
    """
    from rest_framework.mixins import ListModelMixin,CreateModelMixin
    class BookMixinSerializer(GenericAPIView,ListModelMixin,CreateModelMixin):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    
        """ 查询所有数据 """
        def get(self,request):
            return self.list(request)  # 可以进行查看源代码
    
        """ 添加一个数据 """
        def post(self,request):
            return self.create(request)  # 可以进行查看源代码
    
    from rest_framework.mixins import RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
    class BookInfoMixinSerializer(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    
        """ 查看详情 """
        def get(self,request,pk):
            return self.retrieve(request,pk)
    
        """ 更新数据 """
        def put(self,request,pk):
            return self.update(request,pk)
    
        """ 删除数据 """
        def delete(self,request,pk):
            return self.destroy(request,pk)
    

    urls.py

    urlpatterns = [
        ...
        # 扩展视图 Mixins + GenericAPIView
        path("book3/",views.BookMixinSerializer.as_view()),
        re_path("^bookinfo3/(?P<pk>\d+)/$",views.BookInfoMixinSerializer.as_view()),
        ...
    ]
    

    4.1.3 9 个视图子类

    上面的接口代码还可以继续更加的精简,drf在使用GenericAPIViewMixins进行组合以后,还提供了视图子类。视图子类是通用视图类和模型扩展类的子类,提供了各种的视图方法调用mixins操作

    1. ListAPIView = GenericAPIView + ListModelMixin 获取多条数据的视图方法
    2. CreateAPIView = GenericAPIView + CreateModelMixin 添加一条数据的视图方法
    3. RetrieveAPIView = GenericAPIView + RetrieveModelMixin 获取一条数据的视图方法
    4. UpdateAPIView = GenericAPIView + UpdateModelMixin 更新一条数据的视图方法
    5. DestroyAPIView = GenericAPIView + DestroyModelMixin 删除一条数据的视图方法组合视图子类

    组合视图子类:

    只是在一个类里面封装了多个方法(可以看看源码)

    1. ListCreateAPIView = ListAPIView + CreateAPIView 查询多条和新增的组合
    2. RetrieveUpdateAPIView = RetrieveAPIView + UpdateAPIView 查询一条数据和更新的组合
    3. RetrieveDestroyAPIView = RetrieveAPIView + DestroyAPIView 查询一条数据和删除的组合
    4. RetrieveUpdateDestroyAPIView = RetrieveAPIView + UpdateAPIView + DestroyAPIView 查一条数据、新增和删除的组合

    dome_views/urls.py

    urlpatterns = [
    
        ...
    
        # 子类视图
        path("book4/",views.BookModelView.as_view()),
        re_path("^bookinfo4/(?P<pk>\d+)/$",views.BookInfoModelView.as_view())
    ]
    

    dome_views/views.py

    """
    上面的接口代码还可以继续更加的精简,dr f在使用GenericAPIView和Mixins进行组合以后,还提供了视图子类。
    视图子类是通用视图类和模型扩展类的子类,提供了各种的视图方法调用mixins操作
    
        ListAPIView = GenericAPIView + ListModelMixin           获取多条数据的视图方法
        CreateAPIView = GenericAPIView + CreateModelMixin       添加一条数据的视图方法
        RetrieveAPIView = GenericAPIView + RetrieveModelMixin   获取一条数据的视图方法
        UpdateAPIView = GenericAPIView + UpdateModelMixin       更新一条数据的视图方法
        DestroyAPIView = GenericAPIView + DestroyModelMixin     删除一条数据的视图方法组合视图子类
    
    组合视图子类
        ListCreateAPIView = ListAPIView + CreateAPIView
        RetrieveUpdateAPIView = RetrieveAPIView + UpdateAPIView
        RetrieveDestroyAPIView = RetrieveAPIView + DestroyAPIView
        RetrieveUpdateDestroyAPIView = RetrieveAPIView + UpdateAPIView + DestroyAPIView
    
    """
    
    from rest_framework.generics import ListAPIView,CreateAPIView
    
    
    class BookModelView(ListAPIView,CreateAPIView):
        """ 源码内已经封装了get、post方法 """
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    
    
    from rest_framework.generics import RetrieveAPIView,UpdateAPIView,DestroyAPIView
    class BookInfoModelView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
        """ 源码内已经封装了put、get、delete方法 """
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    

    4.2 视图集ViewSets

    使用视图集ViewSet,可以将一系列视图相关的代码逻辑和相关的http请求动作封装到一个类中:

    上面的接口在实现过程中,也存在了代码重复的情况,我们如果合并成一个接口类,则需要考虑2个问题:

    1、路由的合并问题

    2、get方法重复问题

    drf提供了视图集可以解决上面的问题 ViewSet -->APIView中的代码重复问题

    • list()提供一组数据
    • retrieve()提供单个数据
    • create()创建数据
    • update()保存数据
    • destory()删除数据

    ViewSet视图集类不再限制视图方法名只允许get()、 post()等这种情况了,而是实现允许开发者根据自己的需要定

    义自定义方法名,例如 list()、create()等,然后经过路由中使用http和这些视图方法名进行绑定调用。

    视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。如:

    """
    针对视图子类这种写法写法虽然已经省略了http请求,但是在开发通用5个api接口时,还是会出现需要2个类来实现5个接口的情况。
    
    这主要的原因是2点:
    1.获取多条数据与获取一条数据的http请求重复了。在django中依赖于请求方法来响应不同的http请求
    2.部分接口需要pk值作为ur1地址。
    
    drf为了解决上面的2个问题,提供了视图集和路由集。
    视图集就可以帮我们实现一个视图类响应多种重复的http请求
    路由集就可以帮我们实现自动根据不同的视图方法来生成不同参数的路由地址。
    from rest_framework.viewsets import ViewSet # ViewSet是APIView的子类,是所有drf中的视图集的父类
    
    """
    

    4.2.1 ViewSet 基本视图集

    对于ViewSet 视图集来说,只是改变了 类视图函数方法名和路由中的as_view()

    类视图函数方法名 :可以随便写你想要的方法名

    urls.py中的as_view() :需要带参数,字典类型,例如 as_view({"http请求方式":" 类视图函数方法名" }) ,{'get':'list'}

    源码: ctrl + 鼠标左键 点击 as_view() --> viewsets.py 文件夹 --->ViewSetMixin类 view 方法

    # actions.items()  就是as_view() 传的参数
    for method, action in actions.items():
                   handler = getattr(self, action)  # 用传进来的字符串,绑定方法名
                   setattr(self, method, handler)   # 把该方法绑定在类方法中
    

    实例

    dome_view/views.py:

    """
    针对视图子类这种写法写法虽然已经省略了http请求,但是在开发通用5个api接口时,还是会出现需要2个类来实现5个接口的情况。
    
    这主要的原因是2点:
    1.获取多条数据与获取一条数据的http请求重复了。在django中依赖于请求方法来响应不同的http请求
    2.部分接口需要pk值作为ur1地址。
    
    drf为了解决上面的2个问题,提供了视图集和路由集。
    视图集就可以帮我们实现一个视图类响应多种重复的http请求
    路由集就可以帮我们实现自动根据不同的视图方法来生成不同参数的路由地址。
    from rest_framework.viewsets import ViewSet # ViewSet是APIView的子类,是所有drf中的视图集的父类
    
    """
    
    from rest_framework.viewsets import ViewSet
    
    class BookViewSet(ViewSet):
        def book_list(self,request):
            # 1. 获取所有数据
            instance = BookInfo.objects.all()
            # 2. 实例化序列化器
            serializer = BookInfoSerializers(instance,many=True)
            # 3. 返回结果
            return Response(serializer.data)
    
        def book_create(self,request):
            # 1. 获取用户提交的数据
            data = request.data
            # 2. 实例化序列化器
            resializer = BookInfoSerializers(data=data)
            # 3. 验证数据,保存数据
            resializer.is_valid(raise_exception=True)
            resializer.save()
            # 4. 返回数据
            return Response(resializer.data,status=status.HTTP_201_CREATED)
    
        def book_listinfo(self,request,pk):
            # 1. 通过pk值获取模型数据
            instance = BookInfo.objects.get(id=pk)
            # 2. 实例化序列化器
            serializer = BookInfoSerializers(instance)
            # 3. 返回数据
            return Response(serializer.data)
    
        def book_update(self,request,pk):
            # 1. 通过pk值获取模型数据
            instance = BookInfo.objects.get(id=pk)
            # 2. 获取用户提交的数据
            data = request.data
            # 3. 实例化序列化器
            serializer = BookInfoSerializers(instance,data=data)
            # 4. 验证数据,保存数据
            serializer.is_valid(raise_exception=True)
            serializer.save()
            # 4. 返回数据
            return Response(serializer.data,status=status.HTTP_201_CREATED)
    
        def book_delete(self,request,pk):
            # 1. 通过pk 获取模型数据
            try:
                BookInfo.objects.get(id=pk).delete()
            except BookInfo.DoesNotExist:
                pass
            return Response(status=status.HTTP_204_NO_CONTENT)
    

    dome_views/urls.py

    urlpatterns = [
    
        ......
    
        # 视图集 ViewSet
        path("book5/", views.BookViewSet.as_view({
            "get": "book_list",
            'post': "book_create",
        })),
        re_path('^book5/(?P<pk>\d+)/$',views.BookViewSet.as_view({
            "get": "book_listinfo",
            "put": "book_update",
            "delete": "book_delete",
        }))
    ]
    

    4.2.2 GenericViewSet 通用视图类

    GenericViewSet 类继承自 GenericAPIView,并提供了 get_objectget_queryset 方法和其他通用视图

    基本行为的默认配置,但默认情况不包括任何操作。

    调用接口API
    from rest_framework.viewsets import GenericViewSet
    
    实例

    dome_views/views.py

    """ GenericViewSet 通用视图类 """
    
    from rest_framework.viewsets import GenericViewSet
    
    class BookGenericViewSet(GenericViewSet):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
        def list(self,request):
            # 1. 获取所有数据
            instance = self.get_queryset()
            # 2. 实例化序列化器
            serializer = self.get_serializer(instance,many=True)
            # 3.返回数据
            return Response(serializer.data)
    
        def create(self,request):
            # 1. 获取用户提交的数据
            data = request.data
            # 2. 实例化序列化器
            serializer = self.get_serializer(data=data)
            # 3. 验证数据,保存数据
            serializer.is_valid(raise_exception=True)
            serializer.save()
            # 4. 返回数据
            return Response(serializer.data,status= status.HTTP_201_CREATED)
    
        def listinfo(self,request,pk):
    
            # 1. 通过pk获取模型数据
            instance = self.get_object()
            # 2. 实例化序列化器
            serializer = self.get_serializer(instance)
            # 3. 返回数据
            return Response(serializer.data)
    
        def update(self,request,pk):
            # 1. 获取用户提交的数据
            data = request.data
            # 2. 通过pk获取模型数据
            instance = self.get_object()
            # 3. 实例化序列化器
            serializer = self.get_serializer(instance, data=data)
            # 4. 验证数据,保存数据
            serializer.is_valid(raise_exception=True)
            serializer.save()
            # 5. 返回数据
            return Response(serializer.data, status=status.HTTP_201_CREATED)
    
        def delete(self,request,pk):
            # 1. 通过pk获取模型数据进行删除
            self.get_object().delete()
            # 2. 返回数据
            return Response(status=status.HTTP_204_NO_CONTENT)
    

    dome_views/urls.py:

    urlpatterns = [
    
        ......
    
        # 视图集 GenericViewSet 通用视图集
        path("book6/", views.BookGenericViewSet.as_view({
            "get": "list",
            'post': "create",
        })),
        re_path('^book6/(?P<pk>\d+)/$',views.BookGenericViewSet.as_view({
            "get": "listinfo",
            "put": "update",
            "delete": "delete",
        })),
    ]
    
    GenericViewSet 通用类 + Mixins 混入类

    三行代码搞定以上步骤:

    ​ Mixins 类中 已经给我们封装好了5个基础请求方式,所以可以省略,当然你也可以封装另一个类去调用,具体可以看 目录 4.1.2 5 个视图扩展类

    ​ 再写路由 as_view() 中的参数时,{“http请求方式”:“对应的视图类方法名”},因为Mixins 已经封装好了,所以要写Mixins封装的方法名,你也可以自定义方法名,具体可以看 目录 4.1.2 5 个视图扩展类

    实列

    Views.py:

    """视图集 GenericViewSet 通用类 + Mixins 混入类"""
    """
    from rest_framework.mixins import ListModelMixin获取多条数据,返回响应结果       list
    from rest_framework.mixins import CreateModelMixin添加一条数据,返回响应结果     create
    from rest_framework.mixins import RetrieveModelMixin 获取一条数据,返回响应结果  retrieve
    from rest_framework.mixins import UpdateModelMixin更新一条数据,返回响应结果     update(更新全部字段)和partial_update(更新单个或部分字段,例如修改密码,修改头像)
    from rest framework.mixins import DestroyModelMixin 删除一条数据,返回响应结果   destroy
    """
    
    class bookGenericViewSetMixin(GenericViewSet,ListModelMixin,CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin):
    
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    

    dome_views/urls.py:

    urlpatterns = [
    
        ......
    
        # 视图集 GenericViewSet + Mixins 混入类
        path('book7/',views.BookGenericViewSetMixin.as_view({
            "get":"list",
            "post":"create",
        })),
        re_path("^bookinfo7/(?P<pk>\d+)/$",views.BookGenericViewSetMixin.as_view(
            {
                "get": "retrieve",
                "put": "update",
                "delete": "destroy"
             }
        ))
    

    4.2.3 ReadOnlyModelViewSet 只读模型视图集

    接口API
    from rest_framework.viewsets import ReadOnlyModelViewSet
    

    上面的接口类继承的父类太多了。

    我们可以继续让一些合并的视图集父类让视图继承即可。

    • ReadOnlyModelViewSet = mixins.RetrieveModelMixin + mixins.ListModelMixin + GenericViewSet
    • 获取多条数据
    • 获取一条数据
    实列

    views.py:

    """
    只读模型视图集
    ReadOnlyModelViewSet = mixins.RetrieveModelMixin + mixins.ListModelMixin + GenericViewSet
    - 获取多条数据
    - 获取一条数据
    """
    
    from rest_framework.viewsets import ReadOnlyModelViewSet
    class BookReadOnlyModelViewSet(ReadOnlyModelViewSet,UpdateModelMixin,CreateModelMixin,DestroyModelMixin):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    

    dome_views/urls.py:

    urlpatterns = [
    
        ......
    
        # 视图集 ReadOnlyModelViewSet + Mixins 混入类
        path("book8/",views.BookReadOnlyModelViewSet.as_view({
            "get":"list",
            "post":"create",
        })),
        re_path("bookinfo8/(?P<pk>\d+)/$",views.BookReadOnlyModelViewSet.as_view({
            "get":"retrieve",
            "put":"update",
            "delete":"destroy",
        })),
    

    4.2.4 ModelViewSet 模型视图集

    实现了5个API接口,因为ModelViewSet 继承了Mixins的五个扩展类

    ModelViewSet 源码:

    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        """
        A viewset that provides default `create()`, `retrieve()`, `update()`,
        `partial_update()`, `destroy()` and `list()` actions.
        """
        pass
    
    调用接口API
    from rest_framework.viewsets import ModelViewSet
    
    实例

    views.py:

    """ ModelViewSet 模型视图集 """
    
    from rest_framework.viewsets import ModelViewSet
    
    class BookModelViewSet(ModelViewSet):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    

    doem_views/urls.py:

     urlpatterns = [
    
        ......
    
        # 视图集 ModelViewSet 模型视图集
        path("book9/",views.BookModelViewSet.as_view({
            "get":"list",
            "post":"create",
        })),
        re_path("bookinfo9/(?P<pk>\d+)/$",views.BookModelViewSet.as_view({
            "get":"retrieve",
            "put":"update",
            "delete":"destroy",
        })),
    ]
    

    5. 路由集Routers

    5.1 Routers介绍

    对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers

    来帮助我们快速实现路由信息。如果是非视图集,不需要使用路由集routers

    REST framework提供了两个router,使用方式一致的。结果多一个或少一个根目录ur地址的问题而已

    • SimpleRouter
    • DefaultRouter

    5.2 SimpleRouter & DefaultRouter

    调用API

    from rest_framework.routers import DefaultRouter,SimpleRouter
    

    实例

    dome_views/urls.py

    """ 路由集 Routers """
    from rest_framework.routers import DefaultRouter,SimpleRouter
    # 1. 实例化 路由类
    router = SimpleRouter()
    # 2. 给路由注册视图集
    router.register('book10',views.BookModelViewSet,basename="book10")
    print(router.urls)
    # 3. 把生成的路由列表 和 urlpatterns进行拼接
    urlpatterns += router.urls
    

    5.3 视图集中附加action声明

    在视图集中,如果想要让Router自动帮助我们为自定义的动作生成路由信息,需要使用

    rest_framework.decorators.action装饰器。

    以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。

    action装饰器可以接收两个参数:

    • methods:列表,指定允许哪些http请求方法可以访问当前视图方法

    • detail:布尔值,告诉路由对象在生成路由信息时,是否要自动生成pk值,True表示需要,False表示不需要。

    python 路由前缀/<pk> /action方法名/

    • True表示路径格式是 xxx//action方法名/

    • False表示路径格式是 xxx/action方法名/

    • url_path:字符串,访问视图的url地址,如果不设置,则默认采用视图方法名作为访问后缀

    调用API

    from rest_framework.decorators import action
    

    实例

    """ ModelViewSet 模型视图集 """
    from rest_framework.viewsets import ModelViewSet
    
    from rest_framework.decorators import action
    class BookModelViewSet(ModelViewSet):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializers
    
    
        @action(methods=["get"],detail=True)
        def login_log(self,request,pk):
            # http://127.0.0.1:8000/dome_views/book10/1/login_log/
            """ 用户登录历史记录 """
    
            # 可以通过self.action获取本次客户端请求的视图方法名[viewSet 提供的]
            print(self.action) # login_log
    
            return Response({"msg":"用户登录历史记录"})
    
    
        # 路由对象绘视图集生成路由信息时.品余生成5个基杰api接口这主要呆coute.r只识别5个混入类的原因
        # 而针对我们开发者自定义的视图方法,路由对象不会自动生成路由信息,
        # 所以下面这个login,如果希望被外界访问到,则必须通过action装饰器告诉路由对象要给它生成一个路由信息。
        # action(methods-["get","post"],detail = False,url_path="login")
        # action的参数
        # methods,列表,指定允许哪些http请求方法可以访问当前视图方法
        # detail,布尔值,告诉路由对象在生成路由信息时,是否要自动生成pk值,True表示需要,False表示不需要。
        # url_path,字符串,访问视图的url地址,如果不设置,则默认采用视图方法名作为访问后缀
    
        @action(methods=["get"], detail=False,url_path="user/login")
        def login(self, request):
            # http://127.0.0.1:8000/dome_views/book10/user/login/
            return Response({"msg": "登录成功"})
    

    #附录

    ##常用字段类型

    字段 字段构造方式
    BooleanField BooleanField()
    NullBooleanField NullBooleanField()
    CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
    EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
    RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
    SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
    URLField URLField(max_length=200, min_length=None, allow_blank=False)
    UUIDField UUIDField(format='hex_verbose') format:1)'hex_verbose'如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a"2)'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a"3)'int' 如:"123456789012312313134124512351145145114"4)'urn' 如:"urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
    IPAddressField IPAddressField(protocol='both', unpack_ipv4=False, **options)
    IntegerField IntegerField(max_value=None, min_value=None)
    FloatField FloatField(max_value=None, min_value=None)
    DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
    DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
    DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
    TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
    DurationField DurationField()
    ChoiceField ChoiceField(choices) choices与Django的用法相同
    MultipleChoiceField MultipleChoiceField(choices)
    FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
    ListField ListField(child=, min_length=None, max_length=None)
    DictField DictField(child=)

    ##选项参数

    参数名称 作用
    max_length 最大长度
    min_lenght 最小长度
    allow_blank 是否允许为空
    trim_whitespace 是否截断空白字符
    max_value 最大值
    min_value 最小值

    ##通用参数

    参数名称 说明
    read_only 表明该字段仅用于序列化输出,默认False
    write_only 表明该字段仅用于反序列化输入,默认False
    required 表明该字段在反序列化时必须输入,默认True
    default 反序列化时使用的默认值
    allow_null 表明该字段是否允许传入None,默认False
    validators 该字段使用的验证器,是一个列表类型,值是函数
    error_messages 包含错误编号与错误信息的字典
    label 用于HTML展示API页面时,显示的字段名称
    help_text 用于HTML展示API页面时,显示的字段帮助提示信息

    ##关于pk值

    个人理解

    除了使用APIViewViewSet其余视图类都得使用pk作为传递参数,drf 框架 源码中已经封装了,可以变相理解为model中的主键列

    除了APIViewViewSet类,其他类都继承GenericAPIView类,在简写代码封装中,都用到了get_object()方法,该方法是GenericAPIView类的内置方法,而get_object() 就定义了 pk值。

    如果要调用GenericAPIViewGenericAPIView 的子类,就必须将路由中的传递值改为 pk 。

    如:re_path("^bookinfo4/(?P<pk>\d+)/$",views.BookInfoModelView.as_view())

    不然会报以下错误:

    Expected view BookModelViewSet to be called with a URL keyword argument named "pk". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly.
    
    翻译过来
    应使用名为“pk”的URL关键字参数调用view BookModelViewSet。修复URL配置,或设置“”。视图上的lookup\u field `属性正确。
    
    解决办法:
        1.改掉源码中的 属性 lookup_field = 'pk'  #(不建议)
        2.修改url路由中的传递值
    

    源码:generic.py --> get_object()

    lookup_field = 'pk'   # line 39行   GenericAPIView中的属性
    lookup_url_kwarg = None  #line 40行   GenericAPIView中的属性
    
        def get_object(self):
            """
            Returns the object the view is displaying.
    
            You may want to override this if you need to provide non-standard
            queryset lookups.  Eg if objects are referenced using multiple
            keyword arguments in the url conf.
            """
            queryset = self.filter_queryset(self.get_queryset())
    
            # Perform the lookup filtering.
    
    
            #  解释注释
            """
    
            lookup_field  = 'pk'
            lookup_url_kwarg = None
    
            """
            lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
            #  解释注释
            # lookup_url_kwarg = None or 'pk'
            # lookup_url_kwarg =  'pk'
    
            assert lookup_url_kwarg in self.kwargs, (
                'Expected view %s to be called with a URL keyword argument '
                'named "%s". Fix your URL conf, or set the `.lookup_field` '
                'attribute on the view correctly.' %
                (self.__class__.__name__, lookup_url_kwarg)
            )
    
            filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
            #  解释注释
            # filter_kwargs = {"pk":"get、put、delete中的ID值"}
    
            obj = get_object_or_404(queryset, **filter_kwargs)
    
            # May raise a permission denied
            self.check_object_permissions(self.request, obj)
    
            return obj
    

    ##序列化器嵌套

    在序列化器使用过程中,一般一个序列化器对应一个模型数据。往往因为模型之间会存在外键关联,所以一般在输

    出数据时不仅要获取当前模型的数据,甚至其他模型的数据也需要同时返回,这种情况下,我们可以通过序列化器

    嵌套调用的方式,帮我们把当前模型数据进行转换以外还额可以同时转换外键对应的模型数据。

    为了方便演示,此处我们再次创建一个新的子应用来完成这块内容的学习。

    python manage.py startapp drfdome
    

    注册子应用

    INSTALLED_APPS = [
        ...
        "drfdome",  # 序列化器嵌套
    ]
    

    添加路由 settings.py 同级目录下的 urls.py:

    urlpatterns = [
        ...
        path("drfdome/",include('drfdome.urls')),  # ,视图学习
    ]
    

    在刚刚创建的 drfdome 子应用里 创建 urls.py 和 serializers.py 文件

    第一种,嵌套多个模型

    bookid = BookInfoSerializers(many=True) # many = True 嵌套多个 默认false

    urls.py

    from rest_framework.routers import SimpleRouter 
    from . import views
    # 1.实例化路由集
    router = SimpleRouter() 
    
    # 2.添加路
        # 英雄路由
    router.register('hero',views.HeroInfoModelViewSet,basename="hero")
    # 3.拼接路由
    urlpatterns = [
    
    ] + router.urls
    

    serializers.py

    from rest_framework import serializers
    from books.models import BookInfo,HeroInfo
    
    # 创建书本详情路由
    class BookInfoSerializers(serializers.ModelSerializer):
        class Meta:
            model = BookInfo
            fields = ['title'] # 定义所需要的字段
    # 创建英雄序列化器
    class HeroInfoSerializer(serializers.ModelSerializer):
        # 嵌套书本的序列化器
        bookid = BookInfoSerializers(many=True)  # many = True 嵌套多个 默认是false
        class Meta:
            model = HeroInfo
            fields = "__all__"
    

    drfdome/views.py

    from rest_framework.viewsets import ModelViewSet
    from books.models import BookInfo,HeroInfo
    from .serializers import HeroInfoSerializer
    
    """ 英雄模型视图类"""
    class HeroInfoModelViewSet(ModelViewSet):
        queryset = HeroInfo.objects.all()
        serializer_class = HeroInfoSerializer
    
    第二种,嵌套一个模型

    urls.py

    from rest_framework.routers import SimpleRouter 
    from . import views
    # 1.实例化路由集
    router = SimpleRouter() 
    
    # 2.添加路
        # 英雄路由
    router.register('hero',views.HeroInfoModelViewSet,basename="hero")
    # 3.拼接路由
    urlpatterns = [
    
    ] + router.urls
    

    serializers.py

    from rest_framework import serializers
    from books.models import BookInfo,HeroInfo
    
    # 创建书本详情路由
    class BookInfoSerializers(serializers.ModelSerializer):
        class Meta:
            model = BookInfo
            fields = ['title'] # 定义所需要的字段
    # 创建英雄序列化器
    class HeroInfoSerializer(serializers.ModelSerializer):
        # 嵌套书本的序列化器
        bookid = BookInfoSerializers()
        class Meta:
            model = HeroInfo
            fields = "__all__"
    

    drfdome/views.py

    from rest_framework.viewsets import ModelViewSet
    from books.models import BookInfo,HeroInfo
    from .serializers import HeroInfoSerializer
    
    """ 英雄模型视图类"""
    class HeroInfoModelViewSet(ModelViewSet):
        queryset = HeroInfo.objects.all()
        serializer_class = HeroInfoSerializer
    
    第三种,指定嵌套的模型

    book_name = serializers.CharField (source="bookid.title")

    source = "模型字段名 . <可以是多个模型字段名> . 需要的字段名"

    urls.py

    ...
    
    # 2.添加路由
    
        # 第一种方法
    router.register('hero',views.HeroInfoModelViewSet,basename="hero")
        # 第三种方法
    router.register('hero2',views.HeroInfoModelViewSet2,basename="hero2")
    
    ...
    

    serializers.py

    """ 第三种方法 """
    class HeroInfoSerializer2(serializers.ModelSerializer):
        # 嵌套书本的序列化器
        book_name = serializers.CharField(source="bookid.title")
    
        class Meta:
            model = HeroInfo
            fields = ["id", "book_name", 'name']
    

    views.py

    ...
    
    """ 第三种方法 """
    class HeroInfoModelViewSet2(ModelViewSet):
        queryset = HeroInfo.objects.all()
        serializer_class = HeroInfoSerializer2
    
    第四种,depth深度属性

    urls.py

    ...
        # 第四种方法
    router.register('hero3',views.HeroInfoModelViewSet3,basename="hero3")
    ...
    

    serializers.py

    ...
    """ 第四种方法 """
    class HeroInfoSerializer3(serializers.ModelSerializer):
    
        class Meta:
            model = HeroInfo
            fields = "__all__"
            depth = 1
    

    views.py

    ...
    """ 第四种方法 """
    class HeroInfoModelViewSet3(ModelViewSet):
        queryset = HeroInfo.objects.all()
        serializer_class = HeroInfoSerializer3
    
    第五种,自定义model嵌套

    models.py

    class HeroInfo(models.Model):
        name = models.CharField(max_length=20)
        bookid = models.ForeignKey(to=BookInfo,to_field="id",on_delete=models.CASCADE,related_name="bookinfo",db_constraint=False)  # 定义外键
        class Meta:
            db_table='hero'
        def __str__(self):
            return self.name
    
        @property
        def bookName(self):
            return self.bookinfo.values()
    

    urls.py

    ...
        # 第五种方法
    router.register('hero4',views.HeroInfoModelViewSet4,basename="hero4")
    ...
    

    serialziers.py

    ...
    
    """ 第五种方法 """
    class HeroInfoSerializer4(serializers.ModelSerializer):
    
        class Meta:
            model = HeroInfo
            fields = ["id",'name','bookName']
    

    views.py

    ...
    
    """ 第五种方法 """
    class HeroInfoModelViewSet4(ModelViewSet):
        queryset = HeroInfo.objects.all()
        serializer_class = HeroInfoSerializer4
    

    小练习

    id name description ontime outtime duraiton price
    1 python入门 暂无 13:00 9:00 90 1000
    2 python进阶 暂无 14:20 15:20 60 2000
    3 pythonwebkaifa 暂无 16:20 17:20 30 3000
    # **题目**:
    1.给上面的表格设计一个模型
    2.基于APIView编写基本的五个API接口
    3.基于GenericAPIView编写5个API接口
    4.基于GenericAPIView + Mixins编写5个API接口
    
    评论
    您尚未登录,请 登录 后评论
    共 0 条评论 | 欢迎尬评