Django REST framework视图类深度解析与ModelViewSet实战

Owen
Owen
发布时间:2026-10-13 | 6人浏览过
原创

本文深入解析Django REST framework的视图机制,通过手动实现增删改查功能对比内置视图类的优势。详细讲解ModelViewSet的继承结构、Mixin类的作用及请求方法映射,展示如何利用视图组件简化代码并优化接口逻辑,帮助开发者掌握DRF核心视图用法。

前言

在掌握REST framework封装的视图类之后,我对Python面向对象编程的理解更加深入。

Django REST framework框架内置的视图类充分运用了面向对象封装与继承的特性,提升了开发效率。

接下来,我们将尝试手动实现一个类似DRF内置视图类的功能模块。

目标是实现图书出版社数据的增、删、改、查操作,其中查询功能需区分查询所有出版社和查询特定出版社两种场景。

首先设计简略的数据模型,代码位于models.py文件中:

from django.db import models

class Publisher(models.Model):
  name = models.CharField(max_length=16)

  def __str__(self):
    return self.name


class Author(models.Model):
  name = models.CharField(max_length=16)

  def __str__(self):
    return self.name


class Book(models.Model):
  title = models.CharField(max_length=32)
  CHOICES = ((1, 'Python'), (2, 'Go'), (3, 'Linux'))
  category = models.IntegerField(choices=CHOICES)
  pub_date = models.DateField()
  publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)
  authors = models.ManyToManyField(to='Author')

  def __str__(self):
    return self.title

接着配置路由url:

我们需要将使用主键pk与不使用pk的路由业务进行明确区分。

from django.contrib import admin
from django.urls import path, re_path
from bms import views

urlpatterns = [
  path('admin/', admin.site.urls),
  # 查所有对象,与添加对象
  re_path(r'publishers/$', views.PublisherView.as_view()),
  # 带pk查询具体某一条数据
  re_path(r'publishers/(?P<pk>\d+)/$', views.PublisherDetailView.as_view()), # 出版社详情
]

随后定义序列化类,代码位于自创建的serializers.py文件中:

此处省略了新增时需重写的create方法和更新时需重写的update方法的具体实现。

from bms import models
from rest_framework import serializers


class PublisherModelSerializer(serializers.ModelSerializer):
  class Meta:
    model = models.Publisher
    fields = "__all__"


class AuthorModelSerializer(serializers.ModelSerializer):
  class Meta:
    model = models.Author
    fields = "__all__"

最后是视图views.py的实现代码:

from bms import models
from rest_framework.views import APIView
from rest_framework.response import Response
from bms.serializers import PublisherModelSerializer


##################### 面向对象,手动实现REST framework视图功能##################
class GenericView(APIView):
  '''公用类'''
  queryset = None
  serializer_class = None

  def get_queryset(self):
    # 让每一次请求来的时候都现查一次数据
    print('GeneriView------',self)
    return self.queryset.all()

  def get_object(self, request, pk):
    # 获取具体queryset
    return self.get_queryset().filter(pk=pk)

class ListMixin(object):
  # mixi.n 混合类,不能单独使用,利用python支持多继承
  def get(self, request):
    print('ListMixin------------', self)
    queryset = self.get_queryset()
    ser_obj = self.serializer_class(queryset, many=True)
    return Response(ser_obj.data)


class CreateMixin(object):
  def post(self, request):
    ser_obj = self.serializer_class(data=request.data)
    if ser_obj.is_valid():
      ser_obj.save()
      return Response("ok")
    else:
      return Response(ser_obj.errors)


class RetrieveMixin(object):
  """ 获取具体某一条记录"""
  def retrieve(self, request, pk):
    '''将此get方法与ListMixin中的get方法区分'''
    obj = self.get_object(request, pk).first()
    if obj:
      ser_obj = self.serializer_class(obj)
      return Response(ser_obj.data)
    else:
      return Response("无效的id")


class UpdateMixin(object):
  def put(self, request, pk):
    obj = self.get_object(request, pk).first()
    if obj:
      ser_obj = self.serializer_class(instance=obj, data=request.data, partial=True)
      if ser_obj.is_valid():
        ser_obj.save()
        return Response(ser_obj.data)
      else:
        return Response(ser_obj.errors)
    else:
      return Response("无效的id")


class DestroyMixin(object):
  def delete(self, request, pk):
    obj = self.get_object(request, pk)
    if obj:
      obj.delete()
      return Response("删除成功")
    else:
      return Response("无效的id")


class RetrieveView(GenericView, RetrieveMixin):
  '''因两个get方法相冲图,在此曲线访问查询具体对象的get方法'''
  def get(self, request, pk):
    return self.retrieve(request, pk)


# 出版社
class PublisherView(GenericView, ListMixin, CreateMixin):
  '''查所有出版社,增加出版社'''
  # 只用写配置项
  queryset = models.Publisher.objects.all()
  serializer_class = PublisherModelSerializer


class PublisherDetailView(RetrieveView, UpdateMixin, DestroyMixin):
  '''查询具体某一出版社,编辑,删除'''
  queryset = models.Publisher.objects.all()
  serializer_class = PublisherModelSerializer

通过上述方式实现了基础的增删改查功能,且采用对象封装后,若需添加其他表的查询,仅需编写约6行代码即可实现。

例如:实现作者数据的增删改查功能:

# 作者
class AuthorListView(ListCreateAPIView):
  queryset = models.Author.objects.all()
  serializer_class = AuthorModelSerializer


class AuthorDetailView(RetrieveUpdateDestroyAPIView):
  queryset = models.Author.objects.all()
  serializer_class = AuthorModelSerializer

利用面向对象的封装与继承特性,极大地简化了代码结构,有效减少了代码冗余。

然而,我们手动实现的功能尚不够全面和严谨,Django REST framework提供了封装更完善的内置视图类ModelViewSet。

Django REST framework视图组件

视图组件的主要作用是优化接口逻辑的处理流程。

首先我们来查看ModelViewSet的具体实现:

该视图类继承了5个混合类(Mixin)和一个公共基类。

mixins.py中定义了那五个类,分别封装了增、删、改、查(列表与详情)5个方法,以及其他严谨的功能方法,其结构与上述手动实现类似。

使用这个视图类更加简洁,处理一张表的数据只需定义一个类。

from rest_framework.viewsets import ModelViewSet
class AuthorViewSet(ModelViewSet):
  """
    内部封装了这五个方法
    list()
    create()
    retrieve()
    update()
    destroy()

  """
  queryset = models.Author.objects.all()
  serializer_class = AuthorModelSerializer

需要注意的是,内置类中封装的方法名与HTTP请求方式名并不完全一致。

list() 方法对应查询所有对象的get请求。

create() 方法对应添加数据的post请求。

retrieve() 方法对应查询具体对象的get请求。

update() 方法对应更新数据的put请求。

destroy() 方法对应删除数据的delete请求。

因此,在url配置中需要设置actions参数,使请求方式与视图方法一一对应。

urlpatterns = [
  path('admin/', admin.site.urls),
  # 查所有对象,与添加对象
  re_path(r'publishers/$', views.PublisherView.as_view()),
  # 带pk查询具体某一条数据
  re_path(r'publishers/(?P<pk>\d+)/$', views.PublisherDetailView.as_view()), # 出版社详情

  re_path(r'authors/$', views.AuthorViewSet.as_view(actions={'get': 'list', 'post': 'create'})), # 作者列表
  re_path(r'authors/(?P<pk>\d+)/$', views.AuthorViewSet.as_view(
    actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})
    ), # 作者详情
]

此外,REST framework还封装了路由类,关于authors的路由配置还可以采用以下方式编写:

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('authors', views.AuthorViewSet)

urlpatterns += router.urls

不过这种写法可能导致业务关系不够清晰,因此在实际开发中较少使用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表