Django REST Framework(DRF)框架之视图集ViewSet与路由Router

2,824 阅读3分钟

视图集ViewSet与路由Router

视图集ViewSet

在DRF中,视图集(Viewsets)是一种特殊的视图类,它将多个视图组合在一起,方便进行管理和维护,并提供通用的CRUD操作。

视图集可以分为两种类型:基于Model的视图集和基于普通类的视图集。基于Model的视图集允许快速创建一个RESTful API,它可以执行标准的CRUD操作。而基于普通类的视图集则允许更加灵活地定义自己的API逻辑。

视图集主要有以下几种:

1. ViewSet:扩展APIView,具有更加灵活的操作方式

2. GenericViewSet:可以自定义操作,需要手动指定queryset和serializer_class属性

3. ModelViewSet: 处理与模型相关的操作(list, create, retrieve, update, partial_update, destroy)

4. ReadOnlyModelViewSet:只读操作,相当于ModelViewSet中的list和retrieve方法

路由Router

在Django中,路由用于将请求映射到相应的视图函数上。而在DRF中,路由则用于将请求映射到视图集上。

在DRF中,视图集通常会与路由器(Router)一起使用,以便自动注册URL,并将请求分配给相应的视图集方法。

Routers提供了一种简单的方式来自动生成URL路由,将HTTP请求映射到相应的视图函数上。使用Routers可以大大减少代码量,降低开发难度。

DRF提供了两种路由方式:

1. SimpleRouter: 适用于基本的CRUD操作

2. DefaultRouter:更加灵活,可以支持自定义操作

视图集和路由的基本使用

定义一个模型类

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=6, unique=True, verbose_name='姓名')
    age = models.IntegerField(default=0, verbose_name='年龄')

    class Meta:
        db_table = 'tb_user'
        verbose_name = '用户'
        verbose_name_plural = verbose_name
        
    def __str__(self):
        return "name:%s age:%s" % (self.name, self.age)

定义一个序列化器类

from rest_framework import serializers

from user.models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = '__all__'

定义一个UserViewSet视图集

from rest_framework import viewsets

from user.models import User
from user.utils import UserSerializer


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

将视图集注册到SimpleRouter路由中。接着将路由的urls属性添加到Django的urlpatterns列表中即可。

from user import views

urlpatterns = [

]

# 创建Router对象
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
# 注册视图集
router.register('user', views.UserViewSet, basename='users')
# 打印生成的url配置项
for url in router.urls:
    print(url)

# 将生成的路由配置项添加到urlpatterns列表中
urlpatterns = router.urls

将会自动生成两个Router配置项

<URLPattern '^user/$' [name='users-list']>
<URLPattern '^user/(?P<pk>[^/.]+)/$' [name='users-detail']>

因此,使用DRF的视图集和路由可以快速地构建出完整且灵活的API。

视图集ViewSet的使用

ViewSet

ViewSet继承自APIView与ViewSetMixin,作用与APIView基本类似,提供了身份认证、权限校验、流量管理等。

视图集中的处理方法不再对应请求方式(get、post等)命名,而是以对应的操作(action)命名。

list:提供一组数据
retrieve:提供单个数据
create:创建数据
update:保存数据
destory:删除数据
class UserViewSet(viewsets.ViewSet):
    def list(self, request):
        users = User.objects.all()
        serializer = UserSerializer(users, many=True)
        return Response(serializer.data)

    def create(self, request):
        serializer = UserSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    def retrieve(self, request, pk):
        try:
            user = User.objects.get(id=pk)
        except User.DoesNotExist:
            raise Http404

        serializer = UserSerializer(user)
        return Response(serializer.data)

    def update(self, request, pk):
        try:
            user = User.objects.get(id=pk)
        except User.DoesNotExist:
            raise Http404

        serializer = UserSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)

    def destroy(self, request, pk):
        try:
            user = User.objects.get(id=pk)
        except User.DoesNotExist:
            raise Http404

        user.delete()
        return Response(status=status.HTTP_204_NOT_CONNECTED)

在进行URL配置时,需要明确指明某个请求方式请求某个URL地址时,对应的是视图集中的哪个处理函数。

urlpatterns = [
    re_path(r'^test/$', views.TestView.as_view({
        'get': 'list',
        'post': 'create'
    })),
    re_path(r'^test/(?P<pk>\d+)/$', views.TestView.as_view({
        'get': 'retrieve',
        'put': 'update',
        'delete': 'destroy'
    }))
]

GenericViewSet

GenericViewSet继承自GenericAPIView与ViewSetMixin,可以直接搭配Mixin扩展类使用。

from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet


class TestView(mixins.ListModelMixin,
               mixins.CreateModelMixin,
               mixins.RetrieveModelMixin,
               mixins.UpdateModelMixin,
               mixins.DestroyModelMixin,
               GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
urlpatterns = [
    re_path(r'^test/$', views.TestView.as_view({
        'get': 'list',
        'post': 'create'
    })),
    re_path(r'^test/(?P<pk>\d+)/$', views.TestView.as_view({
        'get': 'retrieve',
        'put': 'update',
        'delete': 'destroy'
    }))
]

ModelViewSet

ModelViewSet继承自GenericViewSet,同时包括ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

from rest_framework import mixins
from rest_framework.viewsets import ModelViewSet

class BookInfoViewSet(ModelViewSet):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
urlpatterns = [
    re_path(r'^test/$', views.TestView.as_view({
        'get': 'list',
        'post': 'create'
    })),
    re_path(r'^test/(?P<pk>\d+)/$', views.TestView.as_view({
        'get': 'retrieve',
        'put': 'update',
        'delete': 'destroy'
    }))
]

ReadOnlyModelViewSet

ReadOnlyModelViewSet继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

from rest_framework.viewsets import ReadOnlyModelViewSet

class TestView(ReadOnlyModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
urlpatterns = [
    re_path(r'^test/$', views.TestView.as_view({
        'get': 'list',
    })),
    re_path(r'^test/(?P<pk>\d+)/$', views.TestView.as_view({
        'get': 'retrieve',
    }))
]

视图集添加其他方法

在视图集中,除了默认的action处理方法之外,还可以添加额外的其他处理方法。

class TestView(ViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def other(self, request, pk):
        user = User.objects.get(id=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)
    re_path(r'^test/$', views.TestView.as_view({
        'get': 'list',
    })),
    re_path(r'^test/(?P<pk>\d+)/$', views.TestView.as_view({
        'get': 'retrieve',
    })),
    re_path(r'^test/other/(?P<pk>\d+)/$', views.TestView.as_view({
        'get': 'other'
    }))

Get http://127.0.0.1:8000/test/other/1/

视图集对象action属性

在视图集中,可以通过视图集对象.action获取所有执行action的操作。

应用场景:重写get_serializer_class和get_queryset,根据不同的action操作返回不同的序列化器类和不同的查询集。

def get_serializer_class(self):
    if self.action == 'list':
        return 'list操作所使用的序列化器类'
    else:
        return '其他操作所使用的序列化器类'

def get_queryset(self):
    if self.action == 'list':
        return 'list操作所使用的查询集'
    else:
        return '其他操作所使用的查询集'
class TestView(ViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def other(self, request, pk):
        user = self.get_object(pk)
        serializer = self.get_serializer(user)
        return Response(serializer.data)

    def get_serializer(self, user):
        if self.action == 'other':
            return UserSerializer(user)
        else:
            return self.serializer_classa

    def get_object(self, pk):
        if self.action == 'other':
            return User.objects.get(id=pk)
        else:
            return self.queryset

路由Router的使用

对于视图集ViewSet,除了可以手动进行URL配置指明请求方式与action处理函数之间的对应关系外,还可以使用路由Router来自动生成路由信息。

SimpleRouter

SimpleRouter为每一条url添加一个斜杠后缀,可以在初始化的时候提供 trailing_slash 参数,并设置为 False

router = SimpleRouter(trailing_slash=False)

SimpleRouter生成URL的方式 在这里插入图片描述

DefaultRouter

DefaultRouter会生成一个根路径/的配置项
		
DefaultRouter生成的每个配置项后都可以跟上.json,直接返回json数据

DefaultRouter生成URL的方式

在这里插入图片描述

基本使用

创建Router对象,并注册视图集。

from rest_framework.routers import SimpleRouter, DefaultRouter

router = SimpleRouter() 或 DefaultRouter()

注册视图集

# prefix:该视图集所有处理函数url地址的统一前缀
# viewset:视图集
# base_name:该视图集所有处理函数路由name的统一前缀
router.register(prefix, viewset, base_name)

添加路由数据,将生成的路由配置项添加到urlpatterns列表中

urlpatterns = [
	
]

# 通过router.urls可以获取路由Router生成的url配置项列表。
urlpatterns += router.urls

完整使用如下:

from user import views

urlpatterns = [

]

# 创建Router对象
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
# 注册视图集
router.register('user', views.UserViewSet, basename='users')
# 打印生成的url配置项
for url in router.urls:
    print(url)

# 将生成的路由配置项添加到urlpatterns列表中
urlpatterns = router.urls

将会生成两个Router配置项

<URLPattern '^user/$' [name='users-list']>
<URLPattern '^user/(?P<pk>[^/.]+)/$' [name='users-detail']>

可以指定提取参数的正则表达式,在视图集中设置lookup_value_regex指定。

class UserViewSet(ModelViewSet):
    # 指定路由Router生成url配置项时,从路径中提取参数的正则表达式
    lookup_value_regex = '\d+'
    queryset = User.objects.all()
    serializer_class = UserSerializer
<URLPattern '^user/$' [name='users-list']>
<URLPattern '^user/(?P<pk>\d+)/$' [name='users-detail']>

settings.py设置使用JSON渲染器

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
    ],
}

访问测试

http://127.0.0.1:8000/user/user/

http://127.0.0.1:8000/user/user/1

自定义方法生成路由

在视图集中,如果想要让路由Router自动生成视图集中额外添加的处理方法的url配置项,需要给额外的处理方法添加rest_framework.decorators.action装饰器。

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

methods:表明该处理方法对应的请求方式,列表传递

detail:表明生成url配置项时,是否需要从路径中提取pk数据
	True:生成url配置项时,需要从url地址中提取pk参数
	False:生成url配置项时,不需要从url地址中提取pk参数
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from user.models import User
from user.utils import UserSerializer


class UserViewSet(viewsets.ModelViewSet):
    lookup_value_regex = '\d+'

    @action(methods=["get"], detail=False, )
    def test1(self, request):
        users = User.objects.all()
        print(users)
        serializer = UserSerializer(users, many=True)
        return Response(serializer.data)

    @action(methods=["get"], detail=True)
    def test2(self, request, pk):
        users = User.objects.get(id=pk)
        serializer = UserSerializer(users)
        return Response(serializer.data)

test1函数

使用 @action 装饰器将方法添加到视图集中,并将其映射到名为/test1/的路由。该方法使用 GET 请求返回所有用户的数据,并使用 UserSerializer 序列化响应数据。

test2函数

使用 @action 装饰器将方法添加到视图集中,并将其映射到名为 /users/<pk>/test2/ 的路由。该方法使用 GET 请求返回具有给定主键值的用户的数据,并使用 UserSerializer 序列化响应数据。

参数提取:

lookup_value_regex 属性设置为 \d+,这表示主键值必须是数字。

<URLPattern '^user/$' [name='users-list']>
<URLPattern '^user/test1/$' [name='users-test1']>
<URLPattern '^user/(?P<pk>\d+)/$' [name='users-detail']>
<URLPattern '^user/(?P<pk>\d+)/test2/$' [name='users-test2']>

访问测试

http://127.0.0.1:8000/user/user/test1/

http://127.0.0.1:8000/user/user/1/test2/