zoukankan      html  css  js  c++  java
  • rest_framework框架实现之(视图,路由,渲染器)

    一视图

    一 在前面我们使用视图时继承的时APIview

    from rest_framework.response import Response
    from rest_framework.pagination import PageNumberPagination

    class PageView(APIView):

    def get(self,request,*args,**kwargs):
    #获取所有数据
    roles=models.Role.objects.all().order_by('id')

    #创建分页对象
    pg=PageNumberPagination()

    #在数据库中获取分页的数据
    page_roles=pg.paginate_queryset(queryset=roles,request=request,view=self)
    #对数据进行序列化
    ser = PagerSerialiser(instance=page_roles, many=True)
    # return Response(ser.data)
    return pg.get_paginated_response(ser.data)

    二 我们可以使用 GenericAPIView ,这个类同时也继承了APIview

    #urls
    url(r'^(?P<version>[v1|v2]+)/myview/$', views.MyviewView.as_view()),

    #views
    class MyviewView(GenericAPIView):
    queryset=models.Role.objects.all()
    serializer_class = PagerSerialiser
    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)

    我们来看看它的源码,顺便分析一下

    class GenericAPIView(views.APIView):
        """
        Base class for all other generic views.
        """
        # You'll need to either set these attributes,
        # or override `get_queryset()`/`get_serializer_class()`.
        # If you are overriding a view method, it is important that you call
        # `get_queryset()` instead of accessing the `queryset` property directly,
        # as `queryset` will get evaluated only once, and those results are cached
        # for all subsequent requests.
        queryset = None
        serializer_class = None
    
        # If you want to use object lookups other than pk, set 'lookup_field'.
        # For more complex lookup requirements override `get_object()`.
        lookup_field = 'pk'
        lookup_url_kwarg = None
    
        # The filter backend classes to use for queryset filtering
        filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
    
        # The style to use for queryset pagination.
        pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
    
        def get_queryset(self):
            """
            Get the list of items for this view.
            This must be an iterable, and may be a queryset.
            Defaults to using `self.queryset`.
    
            This method should always be used rather than accessing `self.queryset`
            directly, as `self.queryset` gets evaluated only once, and those results
            are cached for all subsequent requests.
    
            You may want to override this if you need to provide different
            querysets depending on the incoming request.
    
            (Eg. return a list of items that is specific to the user)
            """
            assert self.queryset is not None, (
                "'%s' should either include a `queryset` attribute, "
                "or override the `get_queryset()` method."
                % self.__class__.__name__
            )
    
            queryset = self.queryset
            if isinstance(queryset, QuerySet):
                # Ensure queryset is re-evaluated on each request.
                queryset = queryset.all()
            return queryset
    
        def get_object(self):
            """
            Returns the object the view is displaying.
    
            You may want to override this if you need to provide non-standard
            queryset lookups.  Eg if objects are referenced using multiple
            keyword arguments in the url conf.
            """
            queryset = self.filter_queryset(self.get_queryset())
    
            # Perform the lookup filtering.
            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)
            )
    
            filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
            obj = get_object_or_404(queryset, **filter_kwargs)
    
            # May raise a permission denied
            self.check_object_permissions(self.request, obj)
    
            return obj
    
        def get_serializer(self, *args, **kwargs):
            """
            Return the serializer instance that should be used for validating and
            deserializing input, and for serializing output.
            """
            serializer_class = self.get_serializer_class()
            kwargs['context'] = self.get_serializer_context()
            return serializer_class(*args, **kwargs)
    
        def get_serializer_class(self):
            """
            Return the class to use for the serializer.
            Defaults to using `self.serializer_class`.
    
            You may want to override this if you need to provide different
            serializations depending on the incoming request.
    
            (Eg. admins get full serialization, others get basic serialization)
            """
            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
    
        def get_serializer_context(self):
            """
            Extra context provided to the serializer class.
            """
            return {
                'request': self.request,
                'format': self.format_kwarg,
                'view': self
            }
    
        def filter_queryset(self, queryset):
            """
            Given a queryset, filter it with whichever filter backend is in use.
    
            You are unlikely to want to override this method, although you may need
            to call it either from a list view, or from a custom `get_object`
            method if you want to apply the configured filtering backend to the
            default queryset.
            """
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
            return queryset
    
        @property
        def paginator(self):
            """
            The paginator instance associated with the view, or `None`.
            """
            if not hasattr(self, '_paginator'):
                if self.pagination_class is None:
                    self._paginator = None
                else:
                    self._paginator = self.pagination_class()
            return self._paginator
    
        def paginate_queryset(self, queryset):
            """
            Return a single page of results, or `None` if pagination is disabled.
            """
            if self.paginator is None:
                return None
            return self.paginator.paginate_queryset(queryset, self.request, view=self)
    
        def get_paginated_response(self, data):
            """
            Return a paginated style `Response` object for the given output data.
            """
            assert self.paginator is not None
            return self.paginator.get_paginated_response(data)
    GenericAPIview

    通过源码分析,我们得出的结论:

        queryset = None
        serializer_class = None
        pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
    queryset和serializer默认为None
    pagination_class默认为全局配置,它结果还是会在self里面找
    get_queryset调用 queryset
    paginate_queryset调用paginator,而paginator调用 pagination_class
    get_serializer调用 serializer_class

     三 使用GenericViewSet,它继承了ViewSetMixin, generics.GenericAPIView,使用的urls里面需要传参

    #urls
    url(r'^(?P<version>[v1|v2]+)/myview/$', views.MyviewView.as_view({'get':'list'})),  #这里多了一个方法的对应关系

    #views
    from api.utils.serializers.page import PagerSerialiser
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.pagination import PageNumberPagination
    class MyviewView(GenericViewSet):
    queryset=models.Role.objects.all()
    serializer_class = PagerSerialiser
    pagination_class = PageNumberPagination
    def list(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)

     其他有一个类已经将下面的list方法写好了:ListModelMixin ,所以可以改写成

    #urls
    url(r'^(?P<version>[v1|v2]+)/myview/$', views.MyviewView.as_view({'get':'list'})),  #这里多了一个方法的对应关系
    
    #views
    from api.utils.serializers.page import PagerSerialiser
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.pagination import PageNumberPagination
    from rest_framework.mixins import ListModelMixin #ListModelMixin 里面有list方法
    class MyviewView(ListModelMixinGenericViewSet): 
    queryset
    =models.Role.objects.all()
    serializer_class
    = PagerSerialiser
    pagination_class
    = PageNumberPagination

     既然 list(我们理解为查)已经写好了,那么增、删、改是不是也可以这样

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

    有没有集合这些方法的类呢---》有

    四  ModelViewSet

    ModelViewSet,它继承了六个类
    '''
    ModelViewSet继承了许多类
    class ModelViewSet(mixins.CreateModelMixin, #创建
    mixins.RetrieveModelMixin, #获取单条数据
    mixins.UpdateModelMixin, #更新
    mixins.DestroyModelMixin, #删除
    mixins.ListModelMixin, #查看
    GenericViewSet):
    '''
    对于增、删、改以及查单条数据,在urls里面必须有id
    #urls
    url(r'^(?P<version>[v1|v2]+)/myview/(?P<pk>d+)/$', views.MyviewView.as_view({'get':'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
    #views
    from api.utils.serializers.page import PagerSerialiser
    from rest_framework.pagination import PageNumberPagination
    from rest_framework.viewsets import ModelViewSet

    class MyviewView(ModelViewSet):
    queryset = models.Role.objects.all()
    serializer_class = PagerSerialiser
    pagination_class = PageNumberPagination

    总结:

    ModelViewSet继承了增删改查的类以及GenericViewSet,因此,我们写的时候将更加方便
    缺点:当url里面出现查询所有数据和单条数据的时候,你就需要自己做个判断了

     问题:如何能够完美的区别增删改查(单个或所有)我们可以在路由上做文章

    二路由

    根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件。

    from django.conf.urls import url,include
    from api import views
    from rest_framework import routers
    
    router=routers.DefaultRouter()
    router.register(r'luyou',views.MyviewView)
    router.register(r'rt',views.MyviewView)

    urlpatterns=[
      
    url(r'^(?P<version>[v1|v2]+)/',include(router.urls)),

    ]

    #views
    class MyviewView(ModelViewSet):

    queryset = models.Role.objects.all()
    serializer_class = PagerSerialiser
    pagination_class = PageNumberPagination

    它会将上面的路由转化:

    ^api/ ^(?P<version>[v1|v2]+)/ ^luyou/$ [name='role-list']      ---》http://127.0.0.1:8000/api/v1/luyou/
    ^api/ ^(?P<version>[v1|v2]+)/ ^luyou.(?P<format>[a-z0-9]+)/?$ [name='role-list'] --》http://127.0.0.1:8000/api/v1/luyou.json/
    ^api/ ^(?P<version>[v1|v2]+)/ ^luyou/(?P<pk>[^/.]+)/$ [name='role-detail']        -->http://127.0.0.1:8000/api/v1/luyou/1/
    ^api/ ^(?P<version>[v1|v2]+)/ ^luyou/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$ [name='role-detail'] -->http://127.0.0.1:8000/api/v1/luyou/1.json/
    ^api/ ^(?P<version>[v1|v2]+)/ ^rt/$ [name='role-list']                            --->http://127.0.0.1:8000/api/v1/rt/
    ^api/ ^(?P<version>[v1|v2]+)/ ^rt.(?P<format>[a-z0-9]+)/?$ [name='role-list']    --->http://127.0.0.1:8000/api/v1/rt.json
    ^api/ ^(?P<version>[v1|v2]+)/ ^rt/(?P<pk>[^/.]+)/$ [name='role-detail']           --->http://127.0.0.1:8000/api/v1/rt/1/
    ^api/ ^(?P<version>[v1|v2]+)/ ^rt/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$ [name='role-detail'] -->http://127.0.0.1:8000/api/v1/rt/1.json/
    ^api/ ^(?P<version>[v1|v2]+)/ ^$ [name='api-root']                                --->http://127.0.0.1:8000/api/v1/
    ^api/ ^(?P<version>[v1|v2]+)/ ^.(?P<format>[a-z0-9]+)/?$ [name='api-root']       --->http://127.0.0.1:8000/api/v1/.json

     三渲染器

    根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件,它大致有下面四种

    from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer,AdminRenderer,HTMLFormRenderer #分别对应json格式,api格式,表格,html表单格式
    class MyviewView(APIView):
        renderer_classes = [AdminRenderer,BrowsableAPIRenderer]
        def get(self,request,*args,**kwargs):
            roles=models.Role.objects.all()
            pg=MyCursorPagination()
            pager_roles=pg.paginate_queryset(queryset=roles,request=request,view=self)
            ser=PagerSerialiser(instance=pager_roles,many=True)
    
            return Response(ser.data)

    有一点要注意:使用HTMLFormRenderer 的时候容易报错,一般不使用

    'ListSerializer' object is not iterable  #它只能有一个序列化对象

    最后,它的全局配置

    REST_FRAMEWORK = {
         "DEFAULT_RENDERER_CLASSES":[
    'rest_framework.renderers.JSONRenderer',
    'rest_framework.renderers.BrowsableAPIRenderer',
    ]
    }
  • 相关阅读:
    Pyhton学习-Python与中间件之Celery(7)
    Pyhton学习-Python与中间件之RabbitMQ(6)
    Pyhton学习-Python与中间件之Redis(5)
    Pyhton学习-Python操作数据库之Redis(3)
    解决pycharm问题:module 'pip' has no attribute 'main'
    C#调用Python脚本打印pdf文件
    C# 三种打印方式含代码
    python-memcached学习笔记
    django框架使用mysql步骤
    pycharm配置运行django项目步骤
  • 原文地址:https://www.cnblogs.com/mmyy-blog/p/11020221.html
Copyright © 2011-2022 走看看