[Python自学] DRF (2) (视图类的封装)
参考博客:https://www.cnblogs.com/yuanchenqi/articles/8719520.html
一、mixins模块(level-1)
1.需要解决的问题
在 [Python自学] restframework 中,我们实现了publish和book的增删改查(包括单条查询,一共5个视图操作)。
但是如果我们还有很多同样需要实现5种操作的实例种类,那么代码重复量会非常大。如何解决这个问题,restframework已经为我们提供了解决方法。
2.mixins模块
restframework为我们提供了mixins模块:
from rest_framwork import mixins
mixins模块中为我们提供了5个类:
mixins.ListModelMixin # 获取全量数据(对应get) mixins.CreateModelMixin # 插入数据(对应post) mixins.UpdateModelMixin # 更新数据(对应put) mixins.DestroyModelMixin # 删除数据(对应delete) mixins.RetrieveModelMixin # 获取单条数据(对应get)
3.authors对应的两个视图类
from rest_framework import mixins from rest_framework import generics class AuthorView(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): pass class AuthorDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): pass
AuthorView中主要做全量获取和创建一条数据,所以继承ListModelMixin和CreateModelMixin。
GenericAPIView继承自APIView,是使用Mixins的基础。
例如,在AuthorView中,他继承了ListModelMixin类,则相当于自己有了一个叫list的方法(这个list方法就相当于get视图方法),继承了CreateModelMixin类,相当于自己有了一个create的方法(相当于post视图方法),继承了GnericAPIView相当于继承了APIView类。
Authors对应的urls路由条目:
from django.contrib import admin from django.urls import path, re_path from demo import views urlpatterns = [ path('admin/', admin.site.urls), re_path('^publishes/$', views.PublishView.as_view(), name="publish"), re_path('^publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view(), name="publishdetail"), re_path('^books/$', views.BookView.as_view(), name="book"), re_path('^books/(?P<pk>\d+)/$', views.BookDetailView.as_view(), name="bookdetail"), re_path('^authors/$', views.AuthorView.as_view(), name="author"), re_path('^authors/(?P<pk>\d+)/$', views.AuthorDetailView.as_view(), name="authordetail"), ]
4.实现AuthorView类
from rest_framework import mixins from rest_framework import generics from .models import Author # Author序列化类 class AuthorModelSerializers(serializers.ModelSerializer): class Meta: model = Author fields = "__all__" class AuthorView(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): queryset = Author.objects.all() serializer_class = AuthorModelSerializers # 获取全部authors def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) # 插入一条数据 def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
5.实现AuthorDetailView类
class AuthorDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Author.objects.all() serializer_class = AuthorModelSerializers # 获取单条author记录 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)
6.测试结果
获取全量数据:
Postman以get请求访问http://127.0.0.1:8000/authors/:
[ { "id": 1, "name": "leo", "age": 32 }, { "id": 2, "name": "alex", "age": 35 }, { "id": 3, "name": "Jone", "age": 23 }, { "id": 4, "name": "Lucy", "age": 30 } ]
插入一条数据:
Postman以post请求访问http://127.0.0.1:8000/authors/,post数据:
{ "name": "李雷", "age": 90 }
成功返回以上数据,再查询全量数据:
[ { "id": 1, "name": "leo", "age": 32 }, { "id": 2, "name": "alex", "age": 35 }, { "id": 3, "name": "Jone", "age": 23 }, { "id": 4, "name": "Lucy", "age": 30 }, { "id": 5, "name": "李雷", "age": 90 } ]
成功插入数据。
获取一条数据:
Postman发送get请求,http://127.0.0.1:8000/authors/4/:
返回数据:
{ "id": 4, "name": "Lucy", "age": 30 }
修改一条数据:
将id为4的author的年龄修改为33,向http://127.0.0.1:8000/authors/4/发送put请求,数据如下:
{ "name": "Lucy", "age": 33 }
成功返回以上数据,再次查询id为4的author的信息:
{ "id": 4, "name": "Lucy", "age": 33 }
年龄修改成功。
删除一条数据:
删除id为4的数据,发送delete请求到http://127.0.0.1:8000/authors/4/。
返回空值。
查看全量数据:
[ { "id": 1, "name": "leo", "age": 32 }, { "id": 2, "name": "alex", "age": 35 }, { "id": 3, "name": "Jone", "age": 23 }, { "id": 5, "name": "李雷", "age": 90 } ]
成功删除id为4的数据。
二、generic模块(level-2)
除了第一节中使用的mixins模块,在generic模块中还对mixins的使用进行了封装,AuthorView和AuthorDetailView还可以变成如下形式:
1.generic中封装的类
# 单独封装 generics.ListAPIView generics.CreateAPIView generics.RetrieveAPIView generics.UpdateAPIView generics.DestroyAPIView # 两个操作组合封装 generics.ListCreateAPIView generics.RetrieveUpdateAPIView generics.RetrieveDestroyAPIView # 三个操作组合封装 generics.RetrieveUpdateDestroyAPIView
generic将我们可以在视图类中用要的方法全部封装在一起,我们只需要继承就可以了,get、post、put、delete等方法都被封装在其中了。
2.使用这些类
from rest_framework import generics from .models import Author # Author序列化类 class AuthorModelSerializers(serializers.ModelSerializer): class Meta: model = Author fields = "__all__" class AuthorView(generics.ListCreateAPIView): queryset = Author.objects.all() serializer_class = AuthorModelSerializers class AuthorDetailView(generics.RetrieveUpdateDestroyAPIView): queryset = Author.objects.all() serializer_class = AuthorModelSerializers
如上述代码所示,代码变得更加简洁了。我们只需要控制数据源(queryset)以及显示格式(serializer_class)即可。
三、viewsets模块(level-3)
我们可以将AuthorView和AuthorDetailView合并成一个类,能够执行5种操作。
from .models import Author from rest_framework import viewsets # Author序列化类 class AuthorModelSerializers(serializers.ModelSerializer): class Meta: model = Author fields = "__all__" class AuthorViewSet(viewsets.ModelViewSet): queryset = Author.objects.all() serializer_class = AuthorModelSerializers
对应urls路由条目修改为:
from django.contrib import admin from django.urls import path, re_path from demo import views urlpatterns = [ path('admin/', admin.site.urls), re_path('^publishes/$', views.PublishView.as_view(), name="publish"), re_path('^publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view(), name="publishdetail"), re_path('^books/$', views.BookView.as_view(), name="book"), re_path('^books/(?P<pk>\d+)/$', views.BookDetailView.as_view(), name="bookdetail"), re_path('^authors/$', views.AuthorViewSet.as_view({"get": "list", "post": "create"}), name="author"), re_path('^authors/(?P<pk>\d+)/$', views.AuthorViewSet.as_view( {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}), name="authordetail"), ]
还是两个url路由条目,但是指向的是同一个视图类,不同的是视图函数对应的成员方法不同,用字典参数进行映射。
这里最关键的就是as_view()方法的参数。
这里的as_view()是AuthorViewSet中的as_view(),该方法在 ViewSetMixin 类中实现的,该方法对参数进行了处理。
而前面的PublishView等类的as_view()是在APIView类中实现的,是没有对参数进行处理的。
(>‿◠)✌