# 导入 from rest_framework.generics import GenericAPIView 类GenericView 继承了APIView, class GenericAPIView(views.APIView): # 这里面定义了两个参数 queryset = None serializer_class = None # 记住3个方法 def get_queryset(self) # 获取所有数据,查询所有需要用到 def get_object(self): # 查询单个对象,需要用到 def get_serializer(self, *args, **kwargs) ... return serializer_class(*args, **kwargs)# 使用这个方法得到序列化类的对象,可以传selif.get_serializer(instance,data,many) def get_serializer_class(self) ... return self.serializer_class
class BookGeneric(GenericAPIView): queryset = Book.objects.all() serializer_class = BookModelSerializers # 查询所有 def get(self,request,*args, **kwargs): obj_list = self.get_queryset() ser = self.get_serializer(obj_list, many=True) return Response(ser.data) def post(self,request,*args, **kwargs): pass class BookDetailGeneric(GenericAPIView): queryset = Book.objects.all() serializer_class = BookModelSerializers # 修改某一个 def put(self, request, pk): obj = self.get_object().filter(pk=pk).first() ser = self.get_serializer(obj, data=request.data) return Response(ser.data) def get(self, request, pk): pass def delete(self, request,pk): pass
# 导入5个视图扩展类 # mixins.py from rest_framework.mixins import CreateModelMixin,ListModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin
# 查询所有,ListModelMixin, # 新增 CreateModelMixin, # 查询单个 RetrieveModelMixin # 更新某个 UpdateModelMixin # 删除某个 DestoryModelMixin
# 当有了上面5个视图扩展类以后,就可以在我们自己写的视图类中自由组合的继承他们,实现不同的接口功能 # 记得这里写了一个小作业,继承GenericAPIView和一个自己写的类,在新建视图类中只配置那两个参数,即可实现5个接口 # 如下,为一个查询所有和新增的方法 class BookGenericList(GenericAPIView, ListModelMixin, CreateModelMixin): queryset = Book.objects.all() serializer_class = BookModelSerializers def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
现在,改写一下上述视图类,编写视图类继承视图子类实现同样的功能,并且实现了代码少量书写;
# 导入视图子类 # rest_framework.generics from rest_framework.generics import ListAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView,RetrieveAPIView from rest_framework.generics import ListCreateAPIView,RetrieveUpdateAPIView,RetrieveDestroyAPIView from rest_framework.generics import RetrieveUpdateDestroyAPIView
视图类
# 此时我们只需要在自己写的视图类中继承视图子类ListCreateAPIView,就可以实现对数据的查询所有,和增加一条 class BookListCreate(ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookModelSerializers # 删除一条数据 class BookDestory(DestoryAPIView): queryset = Book.objects.all() serializer_class = BookModelSerializers
可以看到视图类继承视图子类之后的视图类写法变简洁了,但是可以看见,上面两个视图类除了类名和继承的类不一样,下面的代码主体一样。使用视图集以后,能否将5个接口写在一个视图类中。请看下边
# 导入视图集类 from rest_framework.viewsets import ModelViewSet,GenericViewSet,ViewSetMixin,ReadOnlyModelViewSet,ViewSet
路由
# 使用视图集的视图类的url path('bookviewset', views.BookViewSet.as_view({'get': 'list', 'post': 'create'})), path('bookdetailviewset/<int:pk>', views.BookViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
实现5个接口的视图类
# 只继承了一个视图集ModelViewSet类就实现了5个接口的功能 class BookViewSet(ModelViewSet): queryset = Book.objects.all() serializer_class = BookModelSerializers
有点绕的东西
class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet): from rest_framework.generics class GenericViewSet(ViewSetMixin, GenericAPIView): pass # 还是要抓住那个cls,以及cls实例化出来的对象 class ViewSetMixin: @classonlymethod def as_view(cls, actions=None, **initkwargs): """ Because of the way class based views create a closure around the instantiated view, we need to totally reimplement `.as_view`, and slightly modify the view function that is created and returned. """ # The name and description initkwargs may be explicitly overridden for # certain route configurations. eg, names of extra actions. cls.name = None cls.description = None cls.suffix = None cls.detail = None cls.basename = None if not actions: raise TypeError("The `actions` argument must be provided when " "calling `.as_view()` on a ViewSet. For example " "`.as_view({'get': 'list'})`") for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r" % ( cls.__name__, key)) # name and suffix are mutually exclusive if 'name' in initkwargs and 'suffix' in initkwargs: raise TypeError("%s() received both `name` and `suffix`, which are " "mutually exclusive arguments." % (cls.__name__)) def view(request, *args, **kwargs): self = cls(**initkwargs) if 'get' in actions and 'head' not in actions: actions['head'] = actions['get'] # We also store the mapping of request methods to actions, # so that we can later set the action attribute. # eg. `self.action = 'list'` on an incoming GET request. self.action_map = actions # Bind methods to actions # This is the bit that's different to a standard view for method, action in actions.items(): handler = getattr(self, action) setattr(self, method, handler) self.request = request self.args = args self.kwargs = kwargs # And continue as usual return self.dispatch(request, *args, **kwargs) # We need to set these on the view function, so that breadcrumb # generation can pick out these bits of information from a # resolved URL. view.cls = cls view.initkwargs = initkwargs view.actions = actions return csrf_exempt(view) ...
# 导自动生成路由的类 from rest_framework.routers import Simplerouter # 实例化得到对象 router = Simplerouter() # 注册路由 router.register('路径',对应的自己写的视图类) # 路由地址的列表 router.urls # 与路由层的urlpatterns 进行拼接 urlpatterns += router.urls 还有一种写法 from django.urls import path,include urlpatterns = [ path('', include(router.urls)), # 也可以拼接路径 path('api/v1/', include(router.urls)), ]