django-rest-framework中在视图集中新增额外行为

1. 序列化器中的代码

from rest_framework import serializers
from .models import BookInfo


def no_negative(value):
    """校验是不是负数"""
    if value == "":
        return value
    elif value < 0:
        raise serializers.ValidationError({"err": "价格不能是负数"})
    return value


class BookInfoSerializer(serializers.ModelSerializer):
    """图书序列化器"""

    class Meta:
        model = BookInfo
        fields = "__all__"  # 映射模型类中存在的所有字段
        extra_kwargs = {
            "bprice": {"validators": [no_negative]}
        }

    def validate_bread(self, value):
        if value == "":
            return value
        if value < 0:
            raise serializers.ValidationError({"err": "阅读量不能为负数"})
        return value

2.  ViewSet代码

# class BookViewSet(ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, viewsets.GenericViewSet):  # GenericViewSet类继承自GenericAPIView
# class BookViewSet(viewsets.ReadOnlyModelViewSet):
class BookViewSet(viewsets.ModelViewSet):
    """ViewSet视图集"""
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def latest(self, request):  # 前端发送get请求此接口
        # 获取图书中的最后一本书
        book = BookInfo.objects.latest("id")
        serializer = self.get_serializer(book)
        return Response(data=serializer.data)

    def bread(self, request, pk):
        # 修改某一本书的阅读量
        book = self.get_object()
        u_bread = request.data.get("bread")
        # 校验数据
        try:
            u_bread = int(u_bread)
        except Exception as err:
            return Response({"err": "您要修改的阅读量格式不对,请输入正整数类型"}, status=status.HTTP_400_BAD_REQUEST)
        if u_bread < 0:
            return Response({"err": "阅读量不能是负数"}, status=status.HTTP_400_BAD_REQUEST)
        # 保存
        book.bread = u_bread
        book.save()
        # 序列化
        serializer = self.get_serializer(book)
        # 响应
        return Response(serializer.data)

3. 路由中需要新增行为对象的路由

from django.urls import path, re_path
from django.views.decorators.csrf import csrf_exempt

from . import views

urlpatterns = [
    # 图书视图集对应的路由ViewSet
    path("vbooks/", csrf_exempt(views.BookViewSet.as_view({"get": "list", "post": "create"}))),
    re_path("^vbooks/(?P<pk>\d+)/$", csrf_exempt(views.BookViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}))),
    # 如果在增删改查之外额外增加的行为,需要单独定义路由
    # 如果此行为不需要pk,那么它就是列表视图, 如果需要pk那么它就是详情视图
    path("vbooks/latest/", csrf_exempt(views.BookViewSet.as_view({"get": "latest"}))),  # 查询最后一本图书---列表视图
    re_path("^vbooks/(?P<pk>\d+)/bread/$", csrf_exempt(views.BookViewSet.as_view({"put": "bread"}))),  # 修改某本图书的阅读量---详情视图
]

4. 路由器必须结合视图集才能使用,路由器默认帮我们生成标准的增删改查5个接口的路由,如果我们新增的行为接口也想让路由器来生成,那么我们只需要为新增的额外行为增加一个装饰器即可,新增的行为接口有两种:列表视图接口、详情视图接口,如果是列表视图:参数detail=False, 如果是详情视图:参数是detai=True  

列表视图: @action(methods=["get"], detail=False)

详情视图:@action(methods=["get"], detail=True)

5. 视图如下:

from rest_framework.decorators import action


class BookViewSet(viewsets.ModelViewSet):
    """ViewSet视图集"""
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    # 路由: books/latest/   请求方法与接口: get: latest
    @action(methods=["get"], detail=False)
    def latest(self, request):  # 前端发送get请求此接口
        # 获取图书中的最后一本书
        book = BookInfo.objects.latest("id")
        serializer = self.get_serializer(book)
        return Response(data=serializer.data)

    # 路由: books/pk/bread/   请求方法与接口: put: bread
    @action(methods=["put"], detail=True)
    def bread(self, request, pk):
        # 修改某一本书的阅读量
        book = self.get_object()
        u_bread = request.data.get("bread")
        # 校验数据
        try:
            u_bread = int(u_bread)
        except Exception as err:
            return Response({"err": "您要修改的阅读量格式不对,请输入正整数类型"}, status=status.HTTP_400_BAD_REQUEST)
        if u_bread < 0:
            return Response({"err": "阅读量不能是负数"}, status=status.HTTP_400_BAD_REQUEST)
        # 保存
        book.bread = u_bread
        book.save()
        # 序列化
        serializer = self.get_serializer(book)
        # 响应
        return Response(serializer.data)

6. 路由如下:

from django.urls import path, re_path
from django.views.decorators.csrf import csrf_exempt
from rest_framework.routers import DefaultRouter

from . import views

urlpatterns = [
    # 图书视图集对应的路由ViewSet
    # path("vbooks/", csrf_exempt(views.BookViewSet.as_view({"get": "list", "post": "create"}))),
    # re_path("^vbooks/(?P<pk>\d+)/$", csrf_exempt(views.BookViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}))),
    # # 如果在增删改查之外额外增加的行为,需要单独定义路由
    # # 如果此行为不需要pk,那么它就是列表视图, 但是列表视图只有list、create
    # path("vbooks/latest/", csrf_exempt(views.BookViewSet.as_view({"get": "latest"}))),  # 查询最后一本图书---列表视图
    # re_path("^vbooks/(?P<pk>\d+)/bread/$", csrf_exempt(views.BookViewSet.as_view({"put": "bread"}))),  # 修改某本图书的阅读量---详情视图
]


router = DefaultRouter()  # 创建路由器, 路由器只能结合视图集一起使用
# basename参数可以不指定,它默认会找视图集中的类属性queryset对应的模型类名小写作为路由别名前缀
# 但是如果视图集中的类属性queryset没有指定,而是指定的方法get_queryset,那么basename参数必须指定
router.register(prefix="vbooks", viewset=views.BookViewSet, basename="bookinfo")  # 注册路由
urlpatterns += router.urls  # 把生成好的路由拼接到urlpatterns中

也就是说通过路由器帮我们自动生成urlpatterns中注释的四行路由地址,超级方便啊

另外:路由器还有另外一个类:SimpleRouter

from rest_framework.routers import DefaultRouter, SimpleRouter

DefaultRouter是SimpleRouter的子类,它俩的唯一区别就是:DefaultRouter能帮我们生成一个跟路由, DefaultRouter返回的跟路由如下

猜你喜欢

转载自blog.csdn.net/weixin_42289273/article/details/114212882