DAY101 - Rest Framework(六)- 视图组件、路由控制和响应器初识

一、视图组件

1.基本视图

class Books(APIView):
    def get(self, request, *args, **kwargs):
        books = models.Book.objects.all()
        response = {'status': 200, 'msg': '查询成功', 'data': None}
        ret = BooksSerializer(books, many=True)
        response['data'] = ret.data
        return JsonResponse(response, safe=False)

    def post(self, request, *args, **kwargs):
        response = {'status': 200, 'msg': '创建成功', 'data': None, 'error': None}
        ret = BooksSerializer(data=request.data)
        if ret.is_valid():
            ret.save()
            response['data'] = ret.data
        else:
            response['error'] = ret.errors
        return JsonResponse(ret.data, safe=False)


class BooksDetail(APIView):
    def get(self, request, id, *args, **kwargs):
        books = models.Book.objects.filter(pk=id).first()
        response = {'status': 200, 'msg': '查询成功', 'data': None}
        if books:
            ret = BooksSerializer(books, many=False)
            response['data'] = ret.data
        else:
            response['msg'] = '查无此信息'
        return JsonResponse(response, safe=False)

    def put(self, request, id, *args, **kwargs):
        books = models.Book.objects.filter(pk=id).first()
        response = {'status': 200, 'msg': '更新成功', 'data': None, 'error': None}
        ret = BooksSerializer(data=request.data, instance=books)
        if ret.is_valid():
            ret.save()
            response['data'] = ret.data
        else:
            response['error'] = ret.errors
        return JsonResponse(ret.data, safe=False)

    def delete(self, request, id, *args, **kwargs):
        books = models.Book.objects.filter(pk=id).first()
        response = {'status': 200, 'msg': '删除成功', 'data': None}
        ret = BooksSerializer(books, many=False)
        response['data'] = ret.data
        books.delete()
        return JsonResponse(response, safe=False)

2.利用mixin类简化视图

基本视图中的有些代码可以进一步简化,比如说表名和序列化对象可以用一个

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


class Books(GenericAPIView, ListModelMixin, CreateModelMixin):
    # queryset和serializer_class是GenericAPIView定义必须要写的
    queryset = models.Book.objects.all()
    serializer_class = BooksSerializer

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

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BooksDetail(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = BooksSerializer
	# 必须传pk,因为这是RetrieveModelMixin等调用的GenericAPIView方法规定的
    def get(self, request, pk):
        return self.retrieve(request, pk)

    def put(self, request, pk):
        return self.update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)

2.1 mixin类源码分析

# ListModelMixin
# 以ListModelMixin举例,它实际上是,把我们写的基本视图中的代码进行了封装,而对于用户只要继承该类,并且引用就行了
class ListModelMixin(object):
    """
    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)

2.2 GenericAPIView源码分析

# GenericAPIView
# mixin类中的一些方法是调用GenericAPIView中的方法,所以视图类如果继承mixin类,就必须也继承GenericAPIView
# 由于GenericAPIView已经继承了APIView,所以视图不必再继承APIView了
class GenericAPIView(views.APIView):
    queryset = None
    serializer_class = None

    lookup_field = 'pk'
    lookup_url_kwarg = None

    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS

    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

    # get_queryset:获得视图类里的queryset表格的所有数据
    def get_queryset(self):
        queryset = self.queryset
        # 判断queryset是否是QuerySet实例化后的对象
        if isinstance(queryset, QuerySet):
            # 如果是QuerySet对象,就再次all()
            queryset = queryset.all()
        return queryset
	
    # get_object:获得单条数据
    def get_object(self):
        # 对所及数据进行过滤
        queryset = self.filter_queryset(self.get_queryset())
		
        ........
        
        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        self.check_object_permissions(self.request, obj)

        return obj
	
    # get_serializer:返回序列化对象
    def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    # get_serializer_class:获得视图里的序列化对象
    def get_serializer_class(self):
		
        ........

        return self.serializer_class

    def get_serializer_context(self):
		........

    def filter_queryset(self, queryset):
		........

    @property
    def paginator(self):
		........

    def paginate_queryset(self, queryset):
		........

    def get_paginated_response(self, data):
		........

3.利用generice类简化代码

虽然已经简化了代码,但是get和post等方法的代码还是重复了,都是一条代码,还可以再一步简略

from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView


class Books(ListCreateAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BooksSerializer


class BooksDetail(RetrieveUpdateDestroyAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BooksSerializer

3.1 ListCreateAPIView源码分析

# ListCreateAPIView:其实是对mixins类的进一步封装,这样视图里就不需用写get和post了,只要继承ListCreateAPIView就可以,RetrieveUpdateDestroyAPIView也是一样的
class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

4.使用ModelViewSet

进一步简化代码后,我们发现,两个视图的代码变得一样了,那么可不可以更进一步封装,drf提供了一个方法可以把两个视图合并。

url(r'^books/',views.Books.as_view({'get':'list','post':'create'})),
url(r'^booksdetail/(?P<pk>\d+)', views.Books.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
from rest_framework.viewsets import ModelViewSet

class Books(ModelViewSet):
    queryset = models.Book.objects.all()
    serialzizer_class = BooksSerializer

4.1ModelViewSet源码分析

# 第一步
# ModelViewSet
# 其实是对所有的mixins类继承
# GenericViewSet才是关键
class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    pass

# 第二步
# GenericViewSet
# 是对ViewSetMixin和GenericAPIView的继承
# 由于继承了GenericAPIView,而GenericAPIView已经继承了APIView,所以视图不必再继承APIView
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    pass
# 第三步
# ViewSetMixin
# ViewSetMixin是对as_view的重写
class ViewSetMixin(object):
   @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        # actions={'get':'retrieve','put':'update','delete':'destroy'}
        # 是路由层传来的参数
        cls.name = None
        cls.description = None
        cls.suffix = None
        cls.detail = None
        cls.basename = None
        
        .......
    	
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)

            self.action_map = actions
			# 对action循环
            # 请求方式:method='get'  对应方法:action='list'
            for method, action in actions.items():
                # 把action的,也就是list的内存地址赋给了handler
                handler = getattr(self, action)
                # 把handler的,也就是list的内存地址赋给了method
                # 也就是说,执行get方法就是执行list方法
                setattr(self, method, handler)

            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get

            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)

二、路由控制

1.自定义路由

url(r'^books/', views.Books.as_view()),
url(r'^booksdetail/(?P<pk>\d+)', views.BooksDetail.as_view()),
class Books(APIView):
    def get(self, request, *args, **kwargs):
        books = models.Book.objects.all()
        response = {'status': 200, 'msg': '查询成功', 'data': None}
        ret = BooksSerializer(books, many=True)
        response['data'] = ret.data
        return JsonResponse(response, safe=False)

    def post(self, request, *args, **kwargs):
        response = {'status': 200, 'msg': '创建成功', 'data': None, 'error': None}
        ret = BooksSerializer(data=request.data)
        if ret.is_valid():
            ret.save()
            response['data'] = ret.data
        else:
            response['error'] = ret.errors
        return JsonResponse(ret.data, safe=False)


class BooksDetail(APIView):
    def get(self, request, id, *args, **kwargs):
        books = models.Book.objects.filter(pk=id).first()
        response = {'status': 200, 'msg': '查询成功', 'data': None}
        if books:
            ret = BooksSerializer(books, many=False)
            response['data'] = ret.data
        else:
            response['msg'] = '查无此信息'
        return JsonResponse(response, safe=False)

    def put(self, request, id, *args, **kwargs):
        books = models.Book.objects.filter(pk=id).first()
        response = {'status': 200, 'msg': '更新成功', 'data': None, 'error': None}
        ret = BooksSerializer(data=request.data, instance=books)
        if ret.is_valid():
            ret.save()
            response['data'] = ret.data
        else:
            response['error'] = ret.errors
        return JsonResponse(ret.data, safe=False)

    def delete(self, request, id, *args, **kwargs):
        books = models.Book.objects.filter(pk=id).first()
        response = {'status': 200, 'msg': '删除成功', 'data': None}
        ret = BooksSerializer(books, many=False)
        response['data'] = ret.data
        books.delete()
        return JsonResponse(response, safe=False)

2.半自动路由(需继承ModelViewSet)

url(r'^books/',views.Books.as_view({'get':'list','post':'create'})),
url(r'^booksdetail/(?P<pk>\d+)', views.Books.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
from rest_framework.viewsets import ModelViewSet

class Books(ModelViewSet):
    queryset = models.Book.objects.all()
    serialzizer_class = BooksSerializer

3.全自动路由

from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
from rest_framework import routers
# 生成一个router对象
router = routers.DefaultRouter()
# 需要传两个参数,第一个参数就是匹配的路径,第二个参数,是视图类
router.register('books',views.Books)
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 不用匹配正则,空字符串就行
    url('',include(router.urls))
]

# url('',include(router.urls))会自动创建以下6个URL
# '^books/$ [name='book-list']'
# '^books\.(?P<format>[a-z0-9]+)/?$ [name='book-list']'
# '^books/(?P<pk>[^/.]+)/$ [name='book-detail']'
# '^books/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='book-detail']'
# '^$ [name='api-root']'
# '^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']'
from rest_framework.viewsets import ModelViewSet

# 视图一定要是继承ModelViewSet的
class Books(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BooksSerializer

三、响应器

1.基本使用

from rest_framework.renderers import  HTMLFormRenderer,BrowsableAPIRenderer,AdminRenderer,JSONRenderer

class Books(APIView):
    # 局部指定使用
    renderer_classes = [AdminRenderer,]
    
# setting.py
# 全局使用
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES':['rest_framework.renderers.JSONRenderer']
}
根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件。
用户请求URL:
    http://127.0.0.1:8000/test/?format=json
    http://127.0.0.1:8000/test.json
            
显示json格式:JSONRenderer

访问URL:

	http://127.0.0.1:8000/test/?format=json
	http://127.0.0.1:8000/test.json
	http://127.0.0.1:8000/test/
 
默认显示格式:BrowsableAPIRenderer(可以修改它的html文件)

访问URL:

	http://127.0.0.1:8000/test/?format=api
	http://127.0.0.1:8000/test.api
	http://127.0.0.1:8000/test/
 

表格方式:AdminRenderer

访问URL:

	http://127.0.0.1:8000/test/?format=admin
	http://127.0.0.1:8000/test.admin
	http://127.0.0.1:8000/test/
 

form表单方式:HTMLFormRenderer

访问URL:

	http://127.0.0.1:8000/test/?format=form
	http://127.0.0.1:8000/test.form
	http://127.0.0.1:8000/test/
 
posted @ 2018-12-17 19:34  藏岚  阅读(210)  评论(0编辑  收藏  举报