drf(九)—视图
说明:drf 的开发一般使用 CBV 进行开发,Django 的原生视图类是 View 类,drf 中使用较普通的是APIView。
1.GenericAPIView
该类继承于APIView,只是封装了一些调用方法,完成我们的常用操作。
使用方式
class View1View(GenericAPIView):
queryset = models.Role.objects
serializer_class = RoleSerializers
pagination_class = PageNumberPagination
def get(self, request, *args, **kwargs):
roles=self.get_queryset() # 获取到数据库中的数据
pager_roles=self.paginate_queryset(roles)
ser=self.get_serializer(instance=pager_roles,many=True)
return Response(ser.data)
使用 Pycharm 查看类的继承关系
说明:本类使用较少不做过多解释。
2.GenericViewSet
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
The GenericViewSet class does not provide any actions by default,
but does include the base set of generic view behavior, such as
the `get_object` and `get_queryset` methods.
"""
pass
ViewSetMixin 优先级高于后面的 GenericAPIView 类,多继承的顺序问题。
因此我们也可以在自定义的视图中直接继承这两个类。
class View2View(GenericViewSet):
def get(self,rewuest,*args,**kwargs):
return Response("....")
函数as_view()
报错,缺少参数,进行修改;
此时,视图的不同已经开始影响了路由系统。
re_path('^api/(?P<version>[v1|v2]+)/viewv2/',view.View2View.as_view({'get':'list'})),
class View2View(GenericViewSet):
def list(self,rewuest,*args,**kwargs):
return Response("....")
修改后并将该方法的名称映射为字典的值。
进行源码剖析查看为何要这样传参。
class ViewSetMixin:
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
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))
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']
self.action_map = actions
for method, action in actions.items():
handler = getattr(self, action)
# 使用反射进行方法。
setattr(self, method, handler)
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
update_wrapper(view, cls, updated=())
update_wrapper(view, cls.dispatch, assigned=())
view.cls = cls
view.initkwargs = initkwargs
view.actions = actions
return csrf_exempt(view) # 使得函数通过CSRF_TOKEN
3.ModelViewSet
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
# 本类继承更多的类。
pass
列表类
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
# 改类帮我们写了list方法,我们不需要在进行编写
其他的类相同。
class View3View(ListModelMixin,GenericViewSet):# 只有查的功能
queryset = models.Role.objects.all()
serializer_class = RoleSerializers
pagination_class = PageNumberPagination
因此只要继承 ModelViewSet 可以直接获得增删改查的方法。
传入需要更新的视图或者查询单条记录的时候使用路由进行传参
总结
-
继承的越多,自己写的代码就越少;
-
影响路由系统,一个 url 会写两个路由;
-
使用建议
-
当对某张表需要具备增删改查的所有操作时使用 ModelViewSet,较好
-
当只实现某项功能的时候可以去单独执行某项 XXXModelMixin;
总结: a.增删改查 ModelViewSet b.增删 CreateModelMixin,DestroyModelMixin GenericViewSet c.复杂逻辑 GenericViewSet 或 APIView
-
当业务功能较为复杂的时候使用APIView
-
补充:
视图的完整继承类图