1230 视图家族类

视图家族

1. 两个基类 views 视图类

1.1 APIView

rest_framework.views.APIView

APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
View: 将请求方式与视图类的同名方法建立映射,完成请求响应

特点

  • 拥有view的所有功能
  • 重写了as_view 局部禁用csrf
  • 重写dispatch 请求,响应,渲染,异常,解析,三大认证
  • 一系列类属性 可以完成视图类的局部配置

View的不同

APIViewView的不同之处在于:

- 传入到视图方法中的是REST framework的`Request`对象,而不是Django的`HttpRequeset`对象;
- 视图方法可以返回REST framework的`Response`对象,视图会为响应数据设置(render)符合前端要求的格式;
- 任何`APIException`异常都会被捕获到,并且处理成合适的响应信息;
- 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。

APIView中仍以常规的类视图定义方法来实现get() 、post(),或者其他请求方式的方法。

from rest_framework.views import APIView
from rest_framework.response import Response

# url(r'^books/$', views.BookListView.as_view()),
class BookListView(APIView):
    def get(self, request):
        books = BookInfo.objects.all()
        serializer = BookInfoSerializer(books, many=True)
        return Response(serializer.data)

1.2 GenericAPIView

from rest_framework.generics import GenericAPIView

特点

继承自APIVIew,增加了对于列表视图和详情视图可能用到的通用支持方法。主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。
  • 继承APIView,所以拥有APIView的所有
  • get_queryset方法,配置queryset类属性,提供类相关的models
  • 在第二条基础上,get_object方法,配置lookup_url_kwargs类属性,提供视图类相关的具体model
  • get_serializer方法,配置serializer_class类属性,提供视图类相关的序列化对象

提供的方法

  • get_object()
get_object(self) 返回详情视图所需的模型类数据对象,默认使用lookup_field参数来过滤queryset。 在试图中可以调用该方法获取详情信息的模型类对象。

若详情访问的模型类对象不存在,会返回404。

该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。
  • get_serializer()
返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。

注意,在提供序列化器对象的时候,REST framework会向对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。
  • get_queryset()
返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写

代码

.all()不能直接写到objects结束,因为objects结束时,不是QuerySet对象,而是Manager对象,但.all()和.filter()后一定会是QuerySet对象
from rest_framework.generics import GenericAPIView
class CarGenericAPIView(GenericAPIView):
    queryset = models.Car.objects.all()
    serializer_class = serializers.CarModelSerializer
    # GenericAPIView的群查
    def get(self,request,*args,**kwargs):
        # car_query = models.Car.objects.filter(is_delete=False).all()
        # 1. 封装了获取queryset对象的get方法,首先要定义
        car_query = self.get_queryset()
        # car_ser = serializers.CarModelSerializer(instance=car_query,many=True)
        # 2. 封装了序列化类,但是也需要指定serializer_class序列化类
        car_ser = self.get_serializer(instance=car_query,many=True)
        return APIResponse(results=car_ser.data)
    
        # 单查
    def get(self,request,*args,**kwargs):
        # pk = kwargs.get('pk')
        # car_obj = models.Car.objects.filter(is_delete=False,pk=pk).first()
        # 通过get_object获取单查的obj对象
        car_obj = self.get_object()
        # car_ser = serializers.CarModelSerializer(car_obj)
        car_ser = self.get_serializer(car_obj)
        return APIResponse(results=car_ser.data)
  • lookup_url_kwarg属性
# 1.url单查时系统默认设置有名分组是'pk',也可更改并自己配置(唯一键的字段名)
lookup_url_kwarg = 'id'     # 使用id当做分组查询url

总结

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

2. 五个扩展类 mixins 视图工具类

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

使用

# mixins视图工具类
from rest_framework.generics import mixins
class CarMixinGenericAPIView(mixins.ListModelMixin,mixins.CreateModelMixin,GenericAPIView):
    queryset = models.Car.objects.all()
    serializer_class = serializers.CarModelSerializer
    # 群查
    def get(self,request,*args,**kwargs):
        # ListModelMixin中的list与之前的一样,所以直接使用
        return self.list(request,*args,**kwargs)
    
    # 单查 (单查与群查不能同时存在)
    def get(self,request,*args,**kwargs):
        # ListModelMixin中的list与之前的一样,所以直接使用
        return self.retrieve(request,*args,**kwargs)

    # 增
    def post(self,request,*args,**kwargs):
        # CreateModelMixin的create与之前的一样,所以直接使用
        return self.create(request,*args,**kwargs)

特点

五个类,六个方法

- 要配合GenericAPIView类使用,将单查、群查、单增,单整体改,单局部改,单删六个接口,封装成retrieve、list、create、update、partial_update、destroy六个方法

原因

六个方法的实现体,调用的方法就是GenericAPIView提供的,所以要配合GenericAPIView类使用

2.1 ListModelMixin (群查)

列表视图扩展类,提供list(request, \*args, \**kwargs)方法快速实现列表视图,返回200状态码。

该Mixin的list方法会对数据进行过滤和分页。

# 群查
    def get(self,request,*args,**kwargs):
        # ListModelMixin中的list与之前的一样,所以直接使用
        return self.list(request,*args,**kwargs)

2.2 CreateModelMixin (单增)

创建视图扩展类,提供create(request, \*args, \**kwargs)方法快速实现创建资源的视图,成功返回201状态码。

如果序列化器对前端发送的数据验证失败,返回400错误。

# 增
    def post(self,request,*args,**kwargs):
        # CreateModelMixin的create与之前的一样,所以直接使用
        return self.create(request,*args,**kwargs)

2.3 RetrieveModelMixin (单查)

详情视图扩展类,提供retrieve(request, \*args, \**kwargs)方法,可以快速实现返回一个存在的数据对象。

如果存在,返回200, 否则返回404。

class BookDetailView(RetrieveModelMixin, GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self, request, pk):
        return self.retrieve(request)

2.4 UpdateModelMixin (局部单改)

更新视图扩展类,提供update(request, \*args, \**kwargs)方法,可以快速实现更新一个存在的数据对象。

同时也提供partial_update(request, \*args, \**kwargs)方法,可以实现局部更新。

成功返回200,序列化器校验数据失败时,返回400错误。

源代码:

class UpdateModelMixin(object):
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

2.5 DestroyModelMixin (删除)

直接删除数据对象,不推荐

删除视图扩展类,提供destroy(request, \*args, \**kwargs)方法,可以快速实现删除一个存在的数据对象。

成功返回204,不存在返回404。

class DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

3. 可用子类视图 generics 工具视图类

from rest_framework import generics
  • 帮我们将不同个数不同种类的mixins与GenericAPIView进行组合
  • 不同的组合帮我们实现好对应的get、post、put、patch、delete方法
  • 需要我们自己配置三个类属性即可:queryset、serializer_class、lookup_url_kwarg
# 工具视图类 (最常用的视图)
from rest_framework.generics import CreateAPIView,ListAPIView,ListCreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
# CreateAPIView 继承了 mixins.CreateModelMixin,GenericAPIView
#     CreateAPIView中帮我们写了post方法     (单增)
#     ListAPIView写了get方法                (群查)
# 而ListCreateAPIView中也包含,所以直接继承,单查,群查,单改,单删一起继承即可
class CarListCreateAPIView(CreateAPIView,ListAPIView,ListCreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializers.CarModelSerializer
    # url单查时系统默认设置有名分组是'pk',也可更改并自己配置(唯一键的字段名)
    lookup_url_kwarg = 'id'     # 使用id当做分组查询url
    # 1.单查时都不需要写get方法继承使用,直接配置queryset与serializer_class即可

    # 2. 删除时,destroy方法默认是字段删除,所以需要重写destroy方法
    #     有删除需求的接口继承DestroyAPIView,重写destroy方法完成字段的删除(is_delet=False)
    def destroy(self, request, *args, **kwargs):
        pass

# 单独完成单查接口
from rest_framework.generics import RetrieveAPIView
class CarRetrieveAPIView(RetrieveAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializers.CarModelSerializer
    lookup_url_kwarg = 'id'     # 使用id当做分组查询url

# 单独完成群查接口
from rest_framework.generics import ListAPIView
class CarListAPIView(ListAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializers.CarModelSerializer

1)CreateAPIView

提供 post 方法

继承自: GenericAPIView、CreateModelMixin

2)ListAPIView (群查)

提供 get 方法

继承自:GenericAPIView、ListModelMixin

# 单独完成群查接口
from rest_framework.generics import ListAPIView
class CarListAPIView(ListAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializers.CarModelSerializer

3)RetireveAPIView (单查)

提供 get 方法

继承自: GenericAPIView、RetrieveModelMixin

# 单独完成单查接口
from rest_framework.generics import RetrieveAPIView
class CarRetrieveAPIView(RetrieveAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializers.CarModelSerializer
    lookup_url_kwarg = 'id'     # 使用id当做分组查询url

4)DestoryAPIView

提供 delete 方法

继承自:GenericAPIView、DestoryModelMixin

5)UpdateAPIView

提供 put 和 patch 方法

继承自:GenericAPIView、UpdateModelMixin

6)RetrieveUpdateAPIView

提供 get、put、patch方法

继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin

7)RetrieveUpdateDestoryAPIView 单查,单整体改,单局部改,单删

提供 get、put、patch、delete方法

继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

8)ListCreateAPIView (群查和单增)

推导代码

class CarView(APIView):
    def get(self):
        obj,
        ser,
        response,
        
class CarView(GenericAPIView):  # 不会出现,中间产物
    queryset,
    serializer_class,
    lookup_url_kwarg,
    def get(self):
        obj,
        ser,
        response,

class CarView(RetrieveModelMixin, GenericAPIView):  # 自定义组合,可以超过九种
    queryset,
    serializer_class,
    lookup_url_kwarg,
    def get(self):
        self.retrieve()
        
class CarView(RetrieveAPIView):  # 最终产物,系统只提供了九种组合,RetrieveAPIView是其中一种
    queryset,
    serializer_class,
    lookup_url_kwarg,

4. 视图集 ViewSet

from rest_framework.viewsets import ModelViewSet六大功能接口

# 视图集

# 1.viewsetmixin重写了as_view方法,解决单查群查问题
from rest_framework.viewsets import ModelViewSet
# ModelViewSet中继承了viewsetmixin和工具视图类的所有类
class CarModelViewSet(ModelViewSet):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializers.CarModelSerializer
    # 实现了六大接口
    
    # 自定义其他接口的方法
    def destroy(self,request,*args,**kwargs):
        pass

# 2.在as_view中设置单查群查的接口方法
    # 将所有请求方法与响应方法名的映射关系交给用户自己配置(自定义映射关系)
    url(r'^v5/cars/$', views.CarModelViewSet.as_view({
        'get':'list',
        'post':'my_post',
    })),
    url(r'v5/cars/(?P<pk>\d+)/$', views.CarModelViewSet.as_view({
        'get':'retrieve',
        'put':'update',
        'patch':'partial_update',
        'delete':'destroy'
    })),

4.1 常用视图集父类

1) ViewSet

继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。

在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

2)GenericViewSet

继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。

3)ModelViewSet

继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

4)ReadOnlyModelViewSet

继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin。

4.2 action属性

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

  • list() 提供一组数据
  • retrieve() 提供单个数据
  • create() 创建数据
  • update() 保存数据
  • destory() 删除数据

ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。

视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。如:

class BookInfoViewSet(viewsets.ViewSet):

    def list(self, request):
        ...

    def retrieve(self, request, pk=None):
        ...

在设置路由时,我们可以如下操作

urlpatterns = [
    url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
    url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
]

action属性

在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。

例如:

# 动态设置序列化器def get_serializer_class(self):
    if self.action == 'create':
        return OrderCommitSerializer
    else:
        return OrderDataSerializer

4.3 视图集自定义群操作

# 视图集
from rest_framework.viewsets import ViewSetMixin, GenericViewSet, ViewSet, ModelViewSet
class CarReadOnlyAPIView(RetrieveModelMixin, ListModelMixin, GenericViewSet):
    # def many_get(self, request, *args, **kwargs):
    #     return self.list(request, *args, **kwargs)
    #
    # def single_get(self, request, *args, **kwargs):
    #     return self.retrieve(request, *args, **kwargs)

    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializers.CarModelSerializer


# 六大接口
class CarModelViewSet(ModelViewSet):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializers.CarModelSerializer
    # 分析:从实际开发角度分析不合理点
    # 1)没有群增,群整体改,群局部改,群删四个接口
    # 2)删除操作视图集默认走的destroy方法是将资源从数据库中删除,通常一个做字段is_delete字段修改表示删除
    # 3)响应的结果只有数据,没有数据状态码和状态信息

    # 解决1,
    # 群整体改,群局部改,全删三个接口可以独立成三个方法
    def many_update(self, request, *args, **kwargs):
        return APIResponse(msg='这个地方是群整体改,你会写!')
    def many_partial_update(self, request, *args, **kwargs):
        return APIResponse(msg='这个地方是群局部改,你会写!')
    def many_destroy(self, request, *args, **kwargs):
        return APIResponse(msg='这个地方是群删,你会写!')
    # 群增与单增必须公用一个接口,都要走create方法 - 重写create方法,用逻辑进行拆分
    def create(self, request, *args, **kwargs):
        request_data = request.data
        if isinstance(request_data, list):
            car_ser = self.get_serializer(data=request_data, many=True)
            car_ser.is_valid(raise_exception=True)
            car_obj = car_ser.save()
            return APIResponse(msg='群增成功', results=self.get_serializer(car_obj, many=True).data)

        return super().create(request, *args, **kwargs)


    # 解决2,destroy方法是完成is_delete字段值修改 - 重写destroy方法,自定义实现体
    def destroy(self, request, *args, **kwargs):
        car_obj = self.get_object()
        car_obj.is_delete = True
        car_obj.save()
        return APIResponse(msg='删除成功')


    # 解决3,让群查有状态码和状态信息 - 重写list方法
    def list(self, request, *args, **kwargs):
        response = super().list(request, *args, **kwargs)
        return APIResponse(results=response.data)

4.4 总结

核心:
	视图集都继承了 ViewSetMixin类,该类重写了as_view方法,相比APIView的as_view方法,额外多出了一个参数actions
	
	as_view({'get': 'list'}) 传入的{'get': 'list'}就被actions接受收,原理是将get请求映射给视图类的list函数进行处理
1)为什么有GenericViewSet和ViewSet两个视图集基类
    GenericViewSet(ViewSetMixin, GenericAPIView),该分支严格满足资源接口
    ViewSet(ViewSetMixin, APIView),该分支满足的接口与资源Model类关系不是特别密切:登录接口、短信验证码接口


2)ReadOnlyModelViewSet,ModelViewSet两个视图集子类,就是做个一堆mixin与GenericViewSet相结合,自己在urls文件中配置as_view设置映射关系

5. 路由Routers

对于视图集ViewSet我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。

REST framework提供了两个router

  • SimpleRouter
  • DefaultRouter

使用方式

1.创建router对象,并注册视图集,例如

# 路由层:外面会遇到这种写法,看到了要认识
from rest_framework.routers import SimpleRouter
router = SimpleRouter()

router.register('v7/cars', views.CarModelViewSet, basename='car')
# router.register('books', views.BookModelViewSet, basename='book')
# router.register('users', views.UserModelViewSet, basename='user')

register(prefix, viewset, base_name)

  • prefix 该视图集的路由前缀
  • viewset 视图集
  • base_name 路由名称的前缀

如上述代码会形成的路由如下:

^books/$    name: book-list
^books/{pk}/$   name: book-detail

2.添加路由数据

可以有两种方式:

urlpatterns = [
    url(r'', include(router.urls))
]
# urlpatterns.extend(router.urls)

urlpatterns = [
    ...
]
urlpatterns += router.urls
posted @ 2019-12-30 23:26  fwzzz  阅读(228)  评论(0编辑  收藏  举报