DRF框架(五)——context传参,二次封装Response类,两个视图基类(APIView/GenericAPIView),视图扩展类(mixins),子类视图(工具视图),视图集(viewsets),工具视图集

复习

1.整体修改与局部修改

# 序列化get (给前端传递参数)  #查询
ser_obj = ModelSerializer(model_obj)   #只传递一个参数,默认是instance的参数,查询数据
# 反序列化post  save() => create()(数据保存到数据库中)  #添加
ser_obj = ModelSerializer(data=model_data)  #传递data参数,保存数据
#反序列化put   save() => update()  #修改(需要传入instance和data参数)
ser_obj = ModelSerializer(instance=model_obj,data=model_data)  #默认partial参数是False,整体修改
#反序列化patch   partial=True => 匹配字段required=True校验条件
ser_obj = ModelSerializer(instance=model_obj,data=model_data,partial=True)  #局部修改

2.群改ListSerializer

ser_obj = ModelSerializer(instance=model_obj,data=model_data,partial=True,many=True)
    
    # 实现群改功能,反序列化情况下的create、update就不再调用ModelSerializer的
    # 而是调用 ModelSerializer.Meta.list_serializer_class 指向的 ListSerializer 类的create、update
    # ListSerializer默认只实现了群增的create,要实现群改,必须重写update
    
  #自定义序列化类,重写update方法
    class MyListSerializer(ListSerializer):
        def update(self, instance, validated_data):
            # print(instance)  # 要更新的对象们: [obj1, obj2, ...]
            # print(validated_data)  # 更新的对象对应的数据们: [{}, {}, ...]
            # print(self.child)  # 服务的模型序列化类 - V2BookModelSerializer
            for index, obj in enumerate(instance):
                self.child.update(obj, validated_data[index])
            return instance
            
     class MyModelSerializer(ModelSerializer):
         class Meta:
             list_serializer_class = MyListSerializer
     
     将两者类建立关联,在MyListSerializer中就可以用self.child拿到MyModelSerializer,进而使用MyModelSerializer中封装好的方法
     cls.Meta.list_serializer_class.child = cls

视图类views.py使用context传递参数给序列化类serializers.py

1)在视图类中实例化序列化对象时,可以设置context内容
2)在序列化类中的局部钩子、全局钩子、create、update方法中,都可以用self.context访问视图类传递过来的内容
需求: # 1) 在视图类views中,可以通过request得到登陆用户request.user # 2) 在序列化类中,要完成数据库数据的校验与入库操作,可能会需要知道当前的登陆用户,但序列化类无法访问request # 3) 在视图类中实例化序列化对象时,将request对象传递进去

视图层:views.py  (使用context传递参数到序列化类中)

class Book(APIView):
    def post(self, request, *args, **kwargs):
        book_ser = serializers.BookModelSerializer(data=request_data,context={'request':request})   #视图层context传递参数
        book_ser.is_valid(raise_exception=True)
        book_result = book_ser.save()
        return Response({
            'status': 0,
            'msg': 'ok',
            'results': serializers.BookModelSerializer(book_result).data
        })

序列化层:serializers.py

class BookModelSerializer(ModelSerializer):
    class Meta:
        model = models.Book
        fields = ('name', 'price')
    def validate_name(self, value):
        print(self.context.get('request').method)  #序列化层接收参数
        return value

二次封装Response类便于以后每次使用Response

自定义APIResponse

from rest_framework.response import Response

class APIResponse(Response):
    def __init__(self, data_status=0, data_msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs):
        # data的初始状态:状态码与状态信息
        data = {
            'status': data_status,
            'msg': data_msg,
        }
        # data的响应数据体
        # results可能是False、0等数据,这些数据某些情况下也会作为合法数据返回
        if results is not None:
            data['results'] = results
        # data响应的其他内容
        # if kwargs is not None:
        #     for k, v in kwargs.items():
        #         setattr(data, k, v)
        data.update(kwargs)

        super().__init__(data=data, status=http_status, headers=headers, exception=exception)   #重写父类Response的__init__方法

视图家族

views:视图
generics:工具视图
mixins:视图工具集
viewsets:视图集


学习曲线
APIView => GenericAPIView => mixins的五大工具类 => generics中的工具视图 => viewsets中的视图集

两个视图基类   APIView和GenericAPIView

1)AIPView

from rest_framework.views import APIView

APIView是REST framework提供的所有视图的基类,继承自Django的View父类。

APIView与View的不同之处在于:

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

支持定义的属性:
authentication_classes 列表或元祖,身份认证类
permissoin_classes 列表或元祖,权限检查类
throttle_classes 列表或元祖,流量控制类

在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)

2)GenericAPIView通用视图类

GenericAPIView是继承APIView的,使用完全兼容APIView,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可以配合一个或多个Mixin扩展类
重点:GenericAPIView在APIView基础上完成了哪些事
 1)get_queryset():从类属性queryset中获得model的queryset数据                                群操作就走get_queryset()方法(包括群查,群增等)
 2)get_object():从类属性queryset中获得model的queryset数据,再通过有名分组pk确定唯一操作对象     单操作就走get_object()方法(包括单查,单增等)
 3)get_serializer():从类属性serializer_class中获得serializer的序列化类

支持的属性:

  • 列表视图与详情视图通过:(重点******)
    1. queryset  指明视图需要的数据(model查询数据)
    2. serializer_class  指明视图使用的序列化器 
  • 列表视图使用:
    1. pagination_class 分页控制类 
    2. filter_backends  过滤控制后端      
  • 详情页视图使用:
    1. lookup_field  自定义主键 有名分组的查询,默认是'pk'
    2. lookup_url_kwarg 查询单一数据时url中的参数关键字名称,默认与look_field相同  

提供的方法:(******)

  1.get_queryset()    从类属性queryset中获得model的queryset数据  

  2.get_object()   从类属性queryset中获得model的queryset数据,再通过有名分组pk来确定唯一操作对象。

  3.get_serializer()  从类属性serializer_class中获得serializer的序列化类,主要用来提供给Mixin扩展类使用

get_serializer源码:

举例:

视图层:views.py

from rest_framework.generics import GenericAPIView
class BookGenericAPIView(GenericAPIView):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer
    lookup_field = 'pk'     #先定义好,单查可以使用,默认是pk  自定义主键的有名分组,如果路由有名分组不是pk,这个属性就要自己设置了
    # 群取
    def get(self, request, *args, **kwargs):
        book_query = self.get_queryset()       #获取queryset数据(model查询数据)
        book_ser = self.get_serializer(book_query, many=True)
        book_data = book_ser.data
        return APIResponse(results=book_data)

    # 单取
    def get(self, request, *args, **kwargs):
        book_query = self.get_object()
        book_ser = self.get_serializer(book_query)
        book_data = book_ser.data
        return APIResponse(results=book_data)

路由层:urls.py

urlpatterns = [
    url(r'^v2/books/$', views.BookGenericAPIView.as_view()),
    url(r'^v2/books/(?P<pk>.*)/$', views.BookGenericAPIView.as_view()),
]

五个视图扩展类  mixins视图工具集-辅助GenericAPIView

作用:提供了几种后端视图(对数据资源的增删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。

1)mixins有五个工具类文件,一共提供了五个工具类,六个工具方法:单查、群查、单增、单删、单整体改、单局部改
2)继承工具类可以简化请求函数的实现体,但是必须继承GenericAPIView需要GenericAPIView类提供序列化器与数据库查询的方法(见上方GenericAPIView基类知识点)
3)工具类的工具方法返回值都是Response类型对象,如果要格式化数据格式再返回给前台,可以通过 response.data 拿到工具方法返回的Response类型对象的响应数据

五个工具类:CreateModelMixin(增加),DestroyModelMixin(删除),ListModelMixin(查询,查queryset),RetrieveModelMixin(查询,查对象,针对于存在"pk"),UpdateModelMixin(修改)

1.ListModelMixin(群查)

列表视图扩展类,提供 list 方法快速实现查询视图,返回200状态码。除了查询,该list方法会对数据进行过滤和分页

2.CreateModelMixin(单增)         #注意:没有群增的方法,需要自己手动写(******)

创建视图扩展类,提供create方法快速创建资源的视图,成功返回201的状态码

3.RetrieveModelMixin(单查)

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

4.UpdateModelMixin(更新,修改)          #只有单整体改和单局部改,没有群整体改和群局部改

更新视图扩展类,提供update方法,可以快速实现更新一个存在的数据对象,同时也提供partial_update方法,可以实现局部更新。

5.DestoryModelMixin(删除)  一般不怎么用到

删除视图扩展类,提供destory方法,可以快速实现删除一个存在数据对象。

代码示例:

视图层:views.py

from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin
class BookMixinGenericAPIView(ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericAPIView):
  #GenericAPIView提供的序列化器和查询的数据 queryset = models.Book.objects.filter(is_delete=False) serializer_class = serializers.BookModelSerializer      #单查和群查 def get(self, request, *args, **kwargs): if 'pk' in kwargs: response = self.retrieve(request, *args, **kwargs) #单查 RetrieveModelMixin方法 else: # mixins提供的list方法的响应对象是Response,将该对象格式化为自定义的APIResponse response = self.list(request, *args, **kwargs) #群查 ListModelMixin # response的数据都存放在response.data中 return APIResponse(results=response.data)   #单增 def post(self, request, *args, **kwargs): response = self.create(request, *args, **kwargs) #CreateModelMixin方法 return APIResponse(results=response.data)   #单整体修改 def put(self, request, *args, **kwargs): response = self.update(request, *args, **kwargs) #UpdateModelMixin return APIResponse(results=response.data)   #单局部修改 def patch(self, request, *args, **kwargs): response = self.partial_update(request, *args, **kwargs) return APIResponse(results=response.data)

路由层:urls.py

urlpatterns = [
       url(r'^v3/books/$', views.BookMixinGenericAPIView.as_view()),
    url(r'^v3/books/(?P<pk>.*)/$', views.BookMixinGenericAPIView.as_view()),
]

常用功能子类视图   工具视图(继承了GenericAPIView和各种Mixins工具类)

 1)工具视图都是GenericAPIView的子类,且不同的子类继承了不同的工具类
 2)工具视图的功能可以满足需求,只需要继承工具视图,并且提供queryset与serializer_class即可

 

视图作用可使用方法父类
ListAPIView 查询多条数据 get GenericAPIView 
ListModelMixin
CreateAPIView 新增一条数据 post GenericAPIView 
CreateModelMixin
RetrieveAPIView 查询一条数据 get GenericAPIView 
RetrieveModelMixin
UpdateAPIView 修改一条数据 put,patch GenericAPIView 
UpdateModelMixin
DestroyAPIView 删除一条数据 delete GenericAPIView 
DestroyModelMixin
RetrieveUpdateAPIView 单查,更新一条数据 get,put,patch GenericAPIView 
RetrieveModelMixin UpdateModelMixin
RetrieveUpdateDestroyAPIView 单查,更新,删除一条数据 get,put,patch,delete GenericAPIView 
RetrieveModelMixin UpdateModelMixin DestroyModelMixin

           ListCreateAPIView              群查,更新一条       get,post                     

视图层:views.py

from rest_framework.generics import ListCreateAPIView, UpdateAPIView
class BookListCreatePIView(ListCreateAPIView, UpdateAPIView):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer

路由层:urls.py

urlpatterns = [
       url(r'^v4/books/$', views.BookListCreatePIView.as_view()),
    url(r'^v4/books/(?P<pk>.*)/$', views.BookListCreatePIView.as_view()),
]

视图集 viewsetmixin  (注意路由设置)

1.常用视图集父类
  ViewSet
  继承自APIView和ViewSetMixin,没有提供任何方法,需要自己写
  GenericViewSet
  继承GenericAPIView和ViewSetMixin,其中GenericAPIView提供了基础方法,可以直接搭配Mixin扩展类使用,所以我们选这个
  ModelViewSet  
  继承GenericViewset,但同时也包括ListModelMixin、CreateModelMixin等mixin扩展类
1)视图集都是默认优先继承ViewSetMixin类,再继承一个视图类(GenericAPIView或APIView) 2)ViewSetMixin提供了重写的as_view()方法,继承视图集的视图类,配置路由时调用as_view()必须传入 请求名-函数名 映射关系字典 eg: url(r'^v5/books/$', views.BookGenericViewSet.as_view({'get': 'my_get_list'})), 表示get请求会交给my_get_list视图函数处理

路由层:urls.py

urlpatterns = [
       # View的as_view():将get请求映射到视图类的get方法
    # ViewSet的as_view({'get': 'my_get_list'}):将get请求映射到视图类的my_get_list方法
    url(r'^v5/books/$', views.BookGenericViewSet.as_view({'get': 'my_get_list'})),
    url(r'^v5/books/(?P<pk>.*)/$', views.BookGenericViewSet.as_view({'get': 'my_get_obj'})),
]

视图层:views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins  #工具集 可以使用list,retrieve等方法
class BookGenericViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, GenericViewSet):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer

    def my_get_list(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def my_get_obj(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

GenericAPIView与APIView两大继承视图的区别

1)GenericViewSet和ViewSet都继承了ViewSetMixin,as_view都可以配置 请求-函数 映射
2)GenericViewSet继承的是GenericAPIView视图类,用来完成标准的 model 类操作接口
3)ViewSet继承的是APIView视图类,用来完成不需要 model 类参与,或是非标准的 model 类操作接口
      post请求在标准的 model 类操作下就是新增接口,登陆的post不满足
      post请求验证码的接口,不需要 model 类的参与
案例:登陆的post请求,并不是完成数据的新增,只是用post提交数据,得到的结果也不是登陆的用户信息,而是登陆的认证信息

工具视图集  (工具集和视图集的混合使用)

路由层:urls.py

urlpatterns = [
       url(r'^v6/books/$', views.BookModelViewSet.as_view({'get': 'list', 'post': 'create'})),
    url(r'^v6/books/(?P<pk>.*)/$', views.BookModelViewSet.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]

视图层:views.py

from rest_framework.viewsets import ModelViewSet
class BookModelViewSet(ModelViewSet):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer

    # 删不是数据库,而是该记录中的删除字段
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()  # type: models.Book
        if not instance:
            return APIResponse(1, '删除失败')  # 实际操作,在此之前就做了判断
        instance.is_delete = True
        instance.save()
        return APIResponse(0, '删除成功')

路由组件(了解)

from django.conf.urls import include
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
# 所有路由与ViewSet视图类的都可以注册,会产生 '^v6/books/$' 和 '^v6/books/(?P<pk>[^/.]+)/$'
router.register('v6/books', views.BookModelViewSet)

urlpatterns = [
    # 第一种添加子列表方式
    url(r'^', include(router.urls)),
]
# 第二种添加子列表方式
# urlpatterns.extend(router.urls)
posted @ 2019-10-20 22:21  只会玩辅助  阅读(2354)  评论(0编辑  收藏  举报