drf中View和router的详解

Posted williamweson

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了drf中View和router的详解相关的知识,希望对你有一定的参考价值。

Rest Framework 视图和路由

因为涉及到视图层面了,而且下面的例子会反复用到request.data,所以我决定带大家稍微看下源码,感兴趣的可以自己深入了解

无论是View还是APIView最开始都是调用as_view()

技术图片

技术图片

技术图片

技术图片

技术图片

大致过了下APIView给我们封装的数据

总结一下

  • 旧的request封装到新request属性_request里
  • 继承APIView,重新封装的request.query_params相当于旧的request.GET
  • request.data相当于旧的request.POST和request.FILES,且支持json数据类型

第一版封装

app/views

class BookView(APIView):
    def get(self, request):
        query_set = Book.objects.all()
        book_ser = BookSerializer(query_set, many=True)
        return Response(book_ser.data)

    def post(self, request):
        query_set = request.data
        book_ser = BookSerializer(data=query_set)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)
        
class BookEditView(APIView):     
    def get(self, request, pk):
        query_set = Book.objects.filter(pk=pk).first()
        book_ser = BookSerializer(query_set)
        return Response(book_ser.data)

    def patch(self, request, pk):
        query_set = Book.objects.filter(pk=pk).first()
        book_ser = BookSerializer(query_set, data=request.data, partial=True)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)

    def delete(self, request, pk):
        query_set = Book.objects.filter(pk=pk).first()
        if query_set:
            query_set.delete()
            return Response("")
        else:
            return Response("删除的书籍不存在")

使用Mixin封装方法

class GenericAPIView(APIView):
    queryset = None
    serializer_class = None

    def get_queryset(self):
        return self.queryset.all()

    def get_serializer(self,*args,**kwargs):
        return self.serializer_class(*args,**kwargs)
class ListModelMixin:

    def list(self,request,*args,**kwargs):
        queryset = self.get_queryset()
        book_sel = self.get_serializer(queryset,many=True)
        return Response(book_sel.data)

class CreateModelMixin:

    def create(self,request,*args,**kwargs):
        book_sel = self.serializer_class(data=request.data)
        if book_sel.is_valid():
            book_sel.save()
            return Response(book_sel.data)
        else:
            return Response(book_sel.errors)

class UpdateModelMixin:

    def update(self,request,pk,*args,**kwargs):
        book_obj = self.get_queryset().filter(pk=pk).first()
        book_sel = self.serializer_class(book_obj,data=request.data,partial=True)
        if book_sel.is_valid():
            book_sel.save()
            return Response(book_sel.data)
        else:
            return Response(book_sel.errors)


class RetrieveModelMixin:

    def retrieve(self,request,pk,*args,**kwargs):
        book_obj = self.get_queryset().filter(pk=pk).first()
        book_sel = self.serializer_class(book_obj)
        return Response(book_sel.data)

class DestroyModelMixin:
    def destroy(self, request, pk, *args, **kwargs):
        queryset = self.get_queryset()
        try:
            queryset.get(pk=pk).delete()
            return Response("")
        except Exception as e:
            return Response("信息有误")

# Create your views here.
class BookEditView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = models.Book.objects
    serializer_class = BookSerializers
    def get(self,request,pk,*args,**kwargs):
        return self.retrieve(request,pk,*args,**kwargs)

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

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


class BookView(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = models.Book.objects
    serializer_class = BookSerializers
    def get(self,request,*args,**kwargs):
        return self.list(request,*args,**kwargs)

    def post(self,request,*args,**kwargs):
        return self.create(request,*args,**kwargs)
    
# 技术点:因为drf中的GenericAPIView提供了queryset和serializer_class,如果要继承GenericAPIView,
# 则必须重写这两个字段,且GenericAPIView提供get_queryset和get_serializer两个方法
# Mixin类不用继承其他API,只是单独提供方法接口,必须跟其他API类混合继承

感觉经过这么一封装,每个类中的方法看起来清爽多了,我们还可以继续封装

第二版封装

# 上面我们写的继承类太长了~~我们再改改

class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
    pass


class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    pass


class BookEditView(RetrieveUpdateDestroyAPIView):
    queryset = models.Book.objects
    serializer_class = BookSerializers
    def get(self,request,pk,*args,**kwargs):
        return self.retrieve(request,pk,*args,**kwargs)

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

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


class BookView(ListCreateAPIView):
    queryset = models.Book.objects
    serializer_class = BookSerializers
    def get(self,request,*args,**kwargs):
        return self.list(request,*args,**kwargs)

    def post(self,request,*args,**kwargs):
        return self.create(request,*args,**kwargs)

感觉只是把类中的继承稍微简化了下,并不是特别的优雅,来康康第三版

第三版封装

我们知道,一般的View执行as_view()不能传入参数,接下来要介绍的ViewSetMixin,重写了as_view(actions),可以传入我们需要的参数

技术图片

urlpatterns = [
    url(r‘^book$‘, BookView.as_view({"get": "list", "post": "create"})),
    url(r‘^retrieve/(?P<pk>d+)$‘, BookEditView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})), //这里要注意的是,使用这种传参的view,传入的动态id要命名为pk
]

urls.py
from rest_framework.viewsets import ViewSetMixin


# class BookView(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
#     queryset = Book.objects.all()
#     serializer_class = BookSerializer
    
    
# 如果我们再定义一个类
class ModelViewSet(ViewSetMixin, ListCreateAPIView):
    pass
class OwnViewSet(ViewSetMixin,RetrieveUpdateDestroyAPIView)

class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
class BookEditView(OwnViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

我们现在的视图就只要写两行就可以了,其实我们写的所有的视图~框架都帮我们封装好了,刚刚上面用的例子都是手动封装

奉献一张图来看下我们的继承顺序~~~

技术图片

drf的路由

我们上面的路由传参写的特别多~~框架也帮我们封装好了~

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"book", BookView)

urlpatterns = [

]
urlpatterns += router.urls

通过框架的路由可以看出,手写的代码几乎没有了,这里提出一点建议,如果自己的业务逻辑不是跟增删改查特别耦合

不建议用drf提供的路由组件,因为这样会暴露很多的接口,不太安全,总之,一般我们很少用到这个组件,还是尽量自己手写

总结

类的继承链越高,所拥有的功能也就越少,可定制化的程度就越高,尽管上面我们用底层的类,特别轻松的实现了功能,

但需要自定制时,还是继承APIView实现自己的业务逻辑,总之一切按照业务逻辑来走

参考链接

https://www.cnblogs.com/GGGG-XXXX/articles/9675911.html

以上是关于drf中View和router的详解的主要内容,如果未能解决你的问题,请参考以下文章

详解drf中的ModelViewSet

drf框架 2 drf框架的请求生命周期(as_view和dispatch方法), 请求解析渲染响应异常, 序列化组件 ,ORM配置回顾(media文件配置),应用在settings.py中(代码片

Django快速开发实践:Drf框架和xadmin配置指北

Vue-Router的使用详解

drf-路由和认证

DRF框架之路由Routers