Django REST framwork-03-使用mixin和基于类的通用(generics)视图

Posted shark_西瓜甜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django REST framwork-03-使用mixin和基于类的通用(generics)视图相关的知识,希望对你有一定的参考价值。

在这里插入图片描述

一、 mixins 和 generics.GenericAPIView

示例 model

# 服务器表
class Server(models.Model):
    hostname = models.CharField(verbose_name='主机名', max_length=128, unique=True)
    manage_ip = models.GenericIPAddressField(verbose_name='管理 IP', null=True, blank=True)
    latest_date = models.DateTimeField(verbose_name='更新时间', default=timezone.now, null=True)
    create_at = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    

# 硬盘表

class Disk(models.Model):
    slot = models.CharField(verbose_name='插槽位', max_length=8)
    model = models.CharField(verbose_name='磁盘型号', max_length=32)
    capacity = models.FloatField(verbose_name='磁盘容量GB')
    pd_type = models.CharField(verbose_name='接口类型', max_length=32)
    server_obj = models.ForeignKey(
      Server, related_name='disk',
       verbose_name='所属服务器', on_delete=models.CASCADE)
       
       

编写类视图

在上一章节中,我们已经知道 ApiView 和 DRF 的 Serializer 结合, 可以很好的处理图片类型的字段,以及可以很好的处理外键字段。
其实,ApiView 是后面我们所要介绍的所有 DRF View 的基类。

generics.GenericAPIView 提供了更多的属性,从而让开发人员写更少的代码,实现更多的功能。这些属性包括但不限于:

  • queryset = None
  • serializer_class = None

注意: 这里使用的序列化类还是之前定义的基于 Model 的 Serializer

from rest_framework.response import Response
from rest_framework import mixins
from rest_framework import generics

from .serializers import ServerSerializer, DiskSerializer
from cmdb.models import Server, Disk

class ServerListView(mixins.ListModelMixin,
                     generics.GenericAPIView):
    """服务器列表页"""

    queryset = Server.objects.all()
    serializer_class = ServerSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
        
        

mixins.ListModelMixin 提供 .list() 方法,用于提供一组数据。
generics.GenericAPIView 提供分页功能,和

配置 URL

在这里插入图片描述

重启 Django 测试

查看页面格式的 API

在这里插入图片描述

在这里插入图片描述

查看 JSON 格式的 API

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J9lDdnHj-1624288957713)(evernotecid://BBD579E0-7127-4377-8E81-47BEA574FA91/appyinxiangcom/25594833/ENResource/p535)]

JSON 数据如下

在这里插入图片描述

关于 mixin 和基础核心类

基类提供核心功能,就是处理请求和响应,有 as_view 方法。

mixin 类提供 .list()操作, 就是处理数据,提供分页,序列化数据等功能。

然后我们使用自定义的类继承这些类,将这些和 get 方法明确地绑定到适当的操作上。

其他 mixin 的操作类

官方示例和好的诠释了这些操作

class SnippetDetail(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

Mixins 总结表

mixinmixin 实现的操作方法对应HTTP请求的方法需要配合实现通用视图中的方法
mixins.ListModelMixin.list() 返回一个queryset的列表GET.get()
mixins.RetrieveModelMixin.retrieve() 返回一个具体实例GET.get()
mixins.CreateModelMixin.create() 创建一个实例POST.post()
mixins.UpdateModelMixin.update() 对某个实例进行更新PUT/PATCH.put()
mixins.DestroyModelMixin.destroy() 删除某个实例DELETE.delete

PUT/PATCH 的区别

patch方法用来更新局部资源,这句话我们该如何理解?

假设我们有一个UserInfo,里面有userId, userName, userGender等10个字段。可你的编辑功能因为需求,在某个特别的页面里只能修改userName,这时候的更新怎么做?

人们通常(为徒省事)把一个包含了修改后userName的完整userInfo对象传给后端,做完整更新。但仔细想想,这种做法感觉有点二,而且真心浪费带宽(纯技术上讲,你不关心带宽那是你土豪)。

于是patch诞生,只传一个userName到指定资源去,表示该请求是一个局部更新,后端仅更新接收到的字段。

而put虽然也是更新资源,但要求前端提供的一定是一个完整的资源对象,理论上说,如果你用了put,但却没有提供完整的UserInfo,那么缺了的那些字段应该被清空

补充:

最后再补充一句,restful只是标准,标准的意思是如果在大家都依此行事的话,沟通成本会很低,开发效率就高。但并非强制(也没人强制得了),所以你说在你的程序里把方法名从put改成patch没有任何影响,那是自然,因为你的后端程序并没有按照标准对两个方法做不同处理,她的表现自然是一样的

PATCH 与 PUT 属性上的一个重要区别还在于:PUT 是幂等的,而 PATCH 不是幂等的。
幂等是一个数学和计算机学概念,在计算机范畴内表示一个操作执行任意次对系统的影响跟一次是相同。


二、 使用基于类的通用视图

使用 mixin 类,我们重写了视图,使用的代码比以前略少,但我们可以更进一步。REST 框架提供了一组已经混合好的通用视图,我们可以使用它来进一步减少我们的 views.py 模块中视图的代码。

部分通用视图源码

# generics.py 模块中

class ListAPIView(mixins.ListModelMixin,
                  GenericAPIView):
    """
    Concrete view for listing a queryset.
    """
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

可以看出, 由于 generics.ListAPIView 继承了 GenericAPIViewmixins.ListModelMixin
并且(也是主要原因)自己本身实现了 .get() 方法,
所以我们自己的视图继承 generics.ListAPIView 后,就不用写 .get() 方法了。

视图使用示例

from rest_framework import generics

from .serializers import ServerSerializer, DiskSerializer
from cmdb.models import Server, Disk


class ServerGenericView(generics.ListAPIView):
    queryset = Server.objects.all()
    serializer_class = ServerSerializer

class DiskGenericView(generics.ListAPIView):
    queryset = Disk.objects.all()
    serializer_class = DiskSerializer

URL

在这里插入图片描述

测试

在这里插入图片描述

在这里插入图片描述

源码解惑

在这里插入图片描述

以上是关于Django REST framwork-03-使用mixin和基于类的通用(generics)视图的主要内容,如果未能解决你的问题,请参考以下文章

Django REST API:使特定权限级别的字段只读

Django Rest Framework 使 OnetoOne 关系感觉就像是一个模型

如何使 Django REST JWT 身份验证与多个 Web 服务器一起扩展?

如何使 Vue.js 将散列密码发布到 Django REST API AbstractBaseUser 自定义模型?

jwt令牌的刷新如何在django REST angular中工作

Django rest framework JWT ,删除 jwt 令牌