• Django restframwork教程之类视图(class-based views)


    我们也可以使用类的views写我们的API,我们将看到这是一个强大的模式,允许我们重用公共功能,让我们的代码整洁

    使用Class-based Views重新改写我们的API

    打开views.py文件,删除之前内容,加入下面的内容

    from snippets.models import Snippet
    from snippets.serializers import SnippetSerializer
    from django.http import Http404
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status
    
    
    class SnippetList(APIView):
        """
        List all snippets, or create a new snippet.
        """
        def get(self, request, format=None):
            snippets = Snippet.objects.all()
            serializer = SnippetSerializer(snippets, many=True)
            return Response(serializer.data)
    
        def post(self, request, format=None):
            serializer = SnippetSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    class SnippetDetail(APIView):
        """
        Retrieve, update or delete a snippet instance.
        """
        def get_object(self, pk):
            try:
                return Snippet.objects.get(pk=pk)
            except Snippet.DoesNotExist:
                raise Http404
    
        def get(self, request, pk, format=None):
            snippet = self.get_object(pk)
            serializer = SnippetSerializer(snippet)
            return Response(serializer.data)
    
        def put(self, request, pk, format=None):
            snippet = self.get_object(pk)
            serializer = SnippetSerializer(snippet, data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
        def delete(self, request, pk, format=None):
            snippet = self.get_object(pk)
            snippet.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
    

    这个看起来代码很简洁,我们也需要重新定义我们的urls.py

    from django.conf.urls import url
    from snippets import views
    from rest_framework.urlpatterns import format_suffix_patterns
    urlpatterns = [
        url(r'^snippets/$', views.SnippetList.as_view()),
        url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
    ]
    urlpatterns = format_suffix_patterns(urlpatterns)
    

    现在我们已经完成了。你可以像之前那样使用curl来测试一下效果

    url -s http://127.0.0.1:8000/snippets/ | jq .
    [
      {
        "id": 1,
        "title": "",
        "code": "foo = "bar"
    ",
        "linenos": false,
        "language": "python",
        "style": "friendly"
      },
      {
        "id": 2,
        "title": "",
        "code": "print "hello, world"
    ",
        "linenos": false,
        "language": "python",
        "style": "friendly"
      },
      {
        "id": 3,
        "title": "",
        "code": "print "hello, world"",
        "linenos": false,
        "language": "python",
        "style": "friendly"
      }
    ]
    
    

    使用mixins

    使用基于类的视图的一大亮点是它允许我们轻松地组成可重用的行为。到目前为止,我们使用的创建/检索/更新/删除操作对于我们创建的任何模型支持的API视图都非常相似。这些常见的行为在REST框架的mixin类中实现。下面让我们看如何使用mixins类来组合视图

    from snippets.models import Snippet
    from snippets.serializers import SnippetSerializer
    from rest_framework import mixins
    from rest_framework import generics
    
    class SnippetList(mixins.ListModelMixin,
                      mixins.CreateModelMixin,
                      generics.GenericAPIView):
        queryset = Snippet.objects.all()
        serializer_class = SnippetSerializer
    
        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 SnippetDetail(mixins.RetrieveModelMixin,
        mixins.UpdateModelMixin,
        mixins.DestroyModelMixin,
        generics.GenericAPIView):
    
        queryset = Snippet.objects.all()
        serializer_class = SnippetSerializer
    
    
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    
    
        def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
    
    
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    

    代码看起来更加简洁,我们使用GenericAPIView 类来提供核心的功能,然后使用mixins类来提供增删改查的功能,我们只需要定义好请求方法,对应的功能直接调用mixins类中的方法即可

    mixins类源码:

    """
    Basic building blocks for generic class based views.
    
    We don't bind behaviour to http method handlers yet,
    which allows mixin classes to be composed in interesting ways.
    """
    from __future__ import unicode_literals
    
    from rest_framework import status
    from rest_framework.response import Response
    from rest_framework.settings import api_settings
    
    
    class CreateModelMixin(object):
        """
        Create a model instance.
        """
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
        def perform_create(self, serializer):
            serializer.save()
    
        def get_success_headers(self, data):
            try:
                return {'Location': data[api_settings.URL_FIELD_NAME]}
            except (TypeError, KeyError):
                return {}
    
    
    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)
    
    
    class RetrieveModelMixin(object):
        """
        Retrieve a model instance.
        """
        def retrieve(self, request, *args, **kwargs):
            instance = self.get_object()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    
    
    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
                # refresh the instance from the database.
                instance = self.get_object()
                serializer = self.get_serializer(instance)
    
            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)
    
    
    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()
    
    

    使用通用 class-based 视图

    使用mixin类,我们重写了视图以使用比以前少一点的代码,但我们可以进一步。 REST框架提供了一组已经混合的通用视图,我们可以使用它们来修剪我们的views.py模块。

    from snippets.models import Snippet
    from snippets.serializers import SnippetSerializer
    from rest_framework import generics
    
    
    class SnippetList(generics.ListCreateAPIView):
        queryset = Snippet.objects.all()
        serializer_class = SnippetSerializer
    
    
    class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
        queryset = Snippet.objects.all()
        serializer_class = SnippetSerializer
    

    这个看起来代码更加简洁

    generics.ListCreateAPIView实际上继承了mixins中的mixins.ListModelMixin,mixins.CreateModelMixin,等于在又把mixins类根据get post put delete请求方式重新封装了一层。更加减少了我们的代码量

    generics类源码:

    """
    Generic views that provide commonly needed behaviour.
    """
    from __future__ import unicode_literals
    
    from django.db.models.query import QuerySet
    from django.http import Http404
    from django.shortcuts import get_object_or_404 as _get_object_or_404
    
    from rest_framework import mixins, views
    from rest_framework.settings import api_settings
    
    
    def get_object_or_404(queryset, *filter_args, **filter_kwargs):
        """
        Same as Django's standard shortcut, but make sure to also raise 404
        if the filter_kwargs don't match the required types.
        """
        try:
            return _get_object_or_404(queryset, *filter_args, **filter_kwargs)
        except (TypeError, ValueError):
            raise Http404
    
    
    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)
    
    
    # Concrete view classes that provide method handlers
    # by composing the mixin classes with the base view.
    
    class CreateAPIView(mixins.CreateModelMixin,
                        GenericAPIView):
        """
        Concrete view for creating a model instance.
        """
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    
    
    class ListAPIView(mixins.ListModelMixin,
                      GenericAPIView):
        """
        Concrete view for listing a queryset.
        """
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
    
    class RetrieveAPIView(mixins.RetrieveModelMixin,
                          GenericAPIView):
        """
        Concrete view for retrieving a model instance.
        """
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    
    
    class DestroyAPIView(mixins.DestroyModelMixin,
                         GenericAPIView):
        """
        Concrete view for deleting a model instance.
        """
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    
    
    class UpdateAPIView(mixins.UpdateModelMixin,
                        GenericAPIView):
        """
        Concrete view for updating a model instance.
        """
        def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
    
        def patch(self, request, *args, **kwargs):
            return self.partial_update(request, *args, **kwargs)
    
    
    class ListCreateAPIView(mixins.ListModelMixin,
                            mixins.CreateModelMixin,
                            GenericAPIView):
        """
        Concrete view for listing a queryset or creating a model instance.
        """
        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 RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
                                mixins.UpdateModelMixin,
                                GenericAPIView):
        """
        Concrete view for retrieving, updating a model instance.
        """
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    
        def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
    
        def patch(self, request, *args, **kwargs):
            return self.partial_update(request, *args, **kwargs)
    
    
    class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
                                 mixins.DestroyModelMixin,
                                 GenericAPIView):
        """
        Concrete view for retrieving or deleting a model instance.
        """
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    
    
    class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                       mixins.UpdateModelMixin,
                                       mixins.DestroyModelMixin,
                                       GenericAPIView):
        """
        Concrete view for retrieving, updating or deleting a model instance.
        """
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    
        def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
    
        def patch(self, request, *args, **kwargs):
            return self.partial_update(request, *args, **kwargs)
    
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    
    
  • 相关阅读:
    [日常摸鱼]bzoj1470[noi2002]Savage
    [日常摸鱼][POI2000]病毒-Tire图(AC自动机)+dfs
    [日常摸鱼]luogu3398仓鼠找sugar-树链剖分
    [日常摸鱼]luogu1613跑路
    [日常摸鱼]bzoj4802 欧拉函数-PollardRho大整数分解算法
    [日常摸鱼]bzoj1444 [JSOI2009]有趣的游戏——AC自动机+矩阵
    [日常摸鱼]bzoj1038 [ZJOI2008]瞭望塔-模拟退火/几何
    [日常摸鱼]poj2420 A Star not a Tree?
    [日常摸鱼]字符串相关
    图论-拓扑排序-应用
  • 原文地址:https://www.cnblogs.com/pycode/p/6508680.html
Copyright © 2020-2023  润新知