day 77

视图家族

views视图类

  • APIView
Copy"""
1.继承View类
2.重写了as_view方法: 局部禁用csrf
3.重写了dispatch方法: 请求, 响应, 解析, 渲染, 异常, 三大认证
4.一系列类属性: 局部和全局配置
作用: drf最底层视图, 所有视图直接或者间接继承了APIView, 理论上可以完成所有接口需求
"""
  • GenericAPIView
Copy"""
1.继承了APiView
2.get_queryset方法, 需要在视图类中配置queryset类属性
3.get_object方法,  可以在视图类中配置lookup_url_kwarg和lookup_field类属性, 默认是按pk
4.get_serializer方法, 需要在视图类中配置serializer_class类属性

总结:GenericAPIView就是在APIView基础上额外提供了三个方法,三个类属性,如果不配合视图工具类,体现不出优势
目的:视图中的增删改查逻辑相似,但操作的资源不一致,操作资源就是操作 资源对象们(queryset)、资源对象(object)以及资源相关的序列化类(serializer),将这三者形成配置,那操作逻辑就一致,就可以进行封装
"""

class GenericAPIView(views.APIView):
    # 需要自己配置的几个属性
    queryset = None
    serializer_class = None
    lookup_field = 'pk'
    lookup_url_kwarg = None

    def get_queryset(self):
        # queryset为None就抛异常, 因此我们要在视图类中自定义queryset属性
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )
        
        ...
        
        return queryset

    def get_object(self):
        queryset = self.filter_queryset(self.get_queryset())

        # 如果设置了就按lookup_url_kwarg, 没有设置就按lookup_field, 也就是"pk" 
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        ...

        return obj

    def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    def get_serializer_class(self):
        # serializer_class为None就抛异常, 因此我们要在视图类中自定义serializer_class属性
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class
Copyfrom rest_framework.generics import GenericAPIView
# 利用GenericAPIView实现单查群查
class CarGenericAPIView(GenericAPIView):
    # 配置queryset
    queryset = models.Car.objects.filter(is_delete=False).all()
    # 配置序列化类
    serializer_class = CarModelSerializer

    # url有名分组的传递关键字参数的key: car_pk = kwargs.get('pk')
    lookup_url_kwarg = 'pk'
    # 进数据库查询的筛选字段: models.Car.objects.filter(pk=car_pk).first
    lookup_field = 'pk'

    # 群查
    def get(self, request, *args, **kwargs):
         car_query = self.get_queryset()
         serializer_obj = self.get_serializer(instance=car_query, many=True)
         return APIResponse(results=serializer_obj.data)

    # 单查
    def get(self, request, *args, **kwargs):
        car_obj = self.get_object()
        serializer_obj = self.get_serializer(instance=car_obj)
        return APIResponse(results=serializer_obj.data)

mixin视图工具类

  • 视图工具类都是基于GenericAPIView来使用的, 因此我们的视图类都要先继承GenericAPIVIew
  • 五个工具类, 六个方法
Copy"""
1.RetrieveModelMixin
    retrieve方法: 单查
    
2.ListModelMixin
    list方法: 群查
    
3.CreateModelMixin
    create方法: 单增
    
4.UpdateModelMixin
    update方法: 整体单改
    partial_update方法: 局部单改
    
5.DestroyModelMixin
    destory方法: 单删
"""

from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, \
    UpdateModelMixin


# mixin视图工具类
class CarReadModelGenericAPIView(ListModelMixin, RetrieveModelMixin, CreateModelMixin, GenericAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = CarModelSerializer
    lookup_url_kwarg = 'pk'

    # 群查: ListModelMixin下面的list方法
    def get(self, request, *args, **kwargs):
         return self.list(request, *args, **kwargs)

    # 单查: RetrieveModelMixin下面的retrieve方法
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    # 单增: CreateModelMixin下面的create方法
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

generics工具视图类

  • 九种组合
Copy"""
1.都继承了GenericAPIView
2.继承了不同组合的Mixin
3.不同的组合实现了get, post, put, patch, delete方法
4.我们自需要配置queryset, serializer_class, lookup_url_kwarg, lookup_field几个类属性即可
"""

from rest_framework.generics import RetrieveAPIView, ListAPIView, RetrieveUpdateDestroyAPIView


# 单查: RetrieveAPIView下面的get方法
class CarRetrieveAPIView(RetrieveAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = CarModelSerializer
    lookup_url_kwarg = 'pk'


# 群查: ListAPIView下面的get方法
class CarListAPIView(ListAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = CarModelSerializer


# 单查, 整体单改, 局部单改, 单删
class CarRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = CarModelSerializer

viewsets视图集

  • ViewSetMixin
Copy"""
视图集都继承ViewSetMixin, 该类重写as_view方法, 相比于APIView的as_view方法(局部禁用csrf), 多了一个参数: actions, as_view(actions={'get': 'list'}), 通过__getattr__和__setattr__给视图类增加了get属性, 该属性指向list方法, 因此向该url提交get请求, 就会触发视图类下面的list方法
ViewSetMixin只提供as_view方法, 没有写dispatch方法, 也没有继承任何类, 因此我们是不能直接拿来用的

"""
class ViewSetMixin:
    def as_view(cls, actions=None, **initkwargs):
    
        ...

        # actions: {'get': 'list'}
        for method, action in actions.items(): 
            # 通过反射获取视图类下面的list方法
            handler = getattr(self, action)
            # 将list方法赋值给get属性
            setattr(self, method, handler)

        ... 

        return csrf_exempt(view)
  • GenericViewSet和ViewSet两个视图集基类
Copy""" 
这两货啥都没写, 就是多继承了一个类, 用来提供dispatch方法
"""

# 该分支满足的接口与资源Model类关系不是特别密切:登录接口、短信验证码接口
class ViewSet(ViewSetMixin, views.APIView):
    pass

# 该分支严格满足资源接口
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    pass
  • ReadOnlyModelViewSet和ModelViewSet两个视图集子类
Copy"""
这两货好像也是啥都没干, 就行进行了Mixin视图工具类和GenericAPIView的组装
"""

class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
                           mixins.ListModelMixin,
                           GenericViewSet):
    pass


class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    pass

路由配置

Copyurlpatterns = [
    url(r'^v4/car/$', views.CarGenericViewSet.as_view({
        'get': 'list',
        'post': 'create',
        'put': 'many_update',
        'patch': 'many_partial_update',
        'delete': 'many_destroy'
    })),

    url(r'^v4/car/(?P<pk>\d+)/$', views.CarGenericViewSet.as_view({
        'get': 'retrieve',
        'put': 'update',
        'patch': 'partial_update',
        'delete': 'destroy'
    }))

]
Copy# DRF的路由组件
from rest_framework.routers import SimpleRouter

# 生成SimpleRouter对象
router = SimpleRouter()
# 参数: 路由, 视图类, 资源名称
router.register('v4/car', views.CarModelViewSet, basename='car')

urlpatterns = [
    url(r'', include(router.urls))
]
posted @ 2020-01-06 00:13  colacheng  阅读(143)  评论(0编辑  收藏  举报
Live2D