• rest framework之视图组件


    一、APIView

       APIView继承的是和django中CBV模式下的View类。View类中的dispatch方法通过反射对不同的请求方法执行不同的函数。而APIView不仅拥有这个特性,而且重要的是是对request进行了重构,以及添加了很多组件,如认证组件、权限组件以及频率组件等。这些都是在APIView中的dispatch方法中添加的:

    (一)dispatch

     def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
            #rest-framework重构request对象
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
            try:
                self.initial(request, *args, **kwargs)
    
                # Get the appropriate handler method
                #这里和CBV一样进行方法的分发
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                response = handler(request, *args, **kwargs)
    
            except Exception as exc:
                response = self.handle_exception(exc)
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response

    APIView中的dispatch方法不仅包含了和CBV模式一样的反射机制,而且对request进行了封装以及初始化各种组件。

    (二)实例

    from rest_framework.response import Response
    class BookView(APIView):
    
        def get(self,request):
    
            book_list=models.Book.objects.all()
            bs=BookModelSerializer(book_list,many=True)
            return Response(bs.data)

    详情参考:https://www.cnblogs.com/shenjianping/p/11383586.html

    二、GenericAPIView

    (一)通用类

    GenericAPIView继承的是APIView,不过在APIView之上又进行了简化:

    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

    显然在GenericAPIView的内部已经实现了get_queryset、get_object、get_serializer、get_serializer_class、paginator等方法,这样只需要自己的视图继承它,并且提供相应的配

    置即可,无需写更多的代码。

    实例:

    class BookView(GenericAPIView):
    
        queryset = models.Book.objects.all()
        serializer_class = BookModelSerializer
    
        def get(self,request):
            queryset= self.get_queryset()
            bs=self.get_serializer(queryset,many=True)
            return Response(bs.data)

      可以看到get请求下的逻辑代码是自己写的,实际关于增、删、改、查这些操作行为,这些视图类可以从 rest_framework.generics导入,这些类是在GenericAPIView之上进行扩展的,例如ListAPIView,它继承了GenericAPIView、ListModelMixin。

    class ListAPIView(mixins.ListModelMixin,
                      GenericAPIView):
        """
        Concrete view for listing a queryset.
        """
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)

    实例:

    from rest_framework.generics import ListAPIView
    
    class BookView(ListAPIView):
    
        queryset = models.Book.objects.all()
        serializer_class = BookModelSerializer

    (二)其它功能通用类

    class CreateAPIView(mixins.CreateModelMixin,
                        GenericAPIView):
        """
        Concrete view for creating a model instance.
        """
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    CreateAPIView
    class RetrieveAPIView(mixins.RetrieveModelMixin,
                          GenericAPIView):
        """
        Concrete view for retrieving a model instance.
        """
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    RetrieveAPIView
    class DestroyAPIView(mixins.DestroyModelMixin,
                         GenericAPIView):
        """
        Concrete view for deleting a model instance.
        """
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    DestroyAPIView
    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)
    UpdateAPIView
    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)
    ListCreateAPIView
    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)
    RetrieveUpdateAPIView
    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)
    RetrieveDestroyAPIView
    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)
    RetrieveUpdateDestroyAPIView

    三、GenericViewSet

     GenericViewSet继承的是ViewSetMixin和GenericAPIView类,显然GenericViewSet的重要改变就是在ViewSetMixin类中。

    class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        """
        The GenericViewSet class does not provide any actions by default,
        but does include the base set of generic view behavior, such as
        the `get_object` and `get_queryset` methods.
        """
        pass
    GenericViewSet
    class ViewSetMixin(object):
        """
        This is the magic.
    
        Overrides `.as_view()` so that it takes an `actions` keyword that performs
        the binding of HTTP methods to actions on the Resource.
    
        For example, to create a concrete view binding the 'GET' and 'POST' methods
        to the 'list' and 'create' actions...
    
        view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
        """
    
        @classonlymethod
        def as_view(cls, actions=None, **initkwargs):
            """
            Because of the way class based views create a closure around the
            instantiated view, we need to totally reimplement `.as_view`,
            and slightly modify the view function that is created and returned.
            """
            # The name and description initkwargs may be explicitly overridden for
            # certain route confiugurations. eg, names of extra actions.
            cls.name = None
            cls.description = None
    
            # The suffix initkwarg is reserved for displaying the viewset type.
            # This initkwarg should have no effect if the name is provided.
            # eg. 'List' or 'Instance'.
            cls.suffix = None
    
            # The detail initkwarg is reserved for introspecting the viewset type.
            cls.detail = None
    
            # Setting a basename allows a view to reverse its action urls. This
            # value is provided by the router through the initkwargs.
            cls.basename = None
    
            # actions must not be empty
            if not actions:
                raise TypeError("The `actions` argument must be provided when "
                                "calling `.as_view()` on a ViewSet. For example "
                                "`.as_view({'get': 'list'})`")
    
            # sanitize keyword arguments
            for key in initkwargs:
                if key in cls.http_method_names:
                    raise TypeError("You tried to pass in the %s method name as a "
                                    "keyword argument to %s(). Don't do that."
                                    % (key, cls.__name__))
                if not hasattr(cls, key):
                    raise TypeError("%s() received an invalid keyword %r" % (
                        cls.__name__, key))
    
            # name and suffix are mutually exclusive
            if 'name' in initkwargs and 'suffix' in initkwargs:
                raise TypeError("%s() received both `name` and `suffix`, which are "
                                "mutually exclusive arguments." % (cls.__name__))
    
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                # We also store the mapping of request methods to actions,
                # so that we can later set the action attribute.
                # eg. `self.action = 'list'` on an incoming GET request.
                self.action_map = actions
    
                # Bind methods to actions
                # This is the bit that's different to a standard view
                for method, action in actions.items():
                    handler = getattr(self, action) #self.list self.create
                    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
    
                # And continue as usual
                return self.dispatch(request, *args, **kwargs)
    
            # take name and docstring from class
            update_wrapper(view, cls, updated=())
    
            # and possible attributes set by decorators
            # like csrf_exempt from dispatch
            update_wrapper(view, cls.dispatch, assigned=())
    
            # We need to set these on the view function, so that breadcrumb
            # generation can pick out these bits of information from a
            # resolved URL.
            view.cls = cls
            view.initkwargs = initkwargs
            view.actions = actions
            return csrf_exempt(view)
    
        def initialize_request(self, request, *args, **kwargs):
            """
            Set the `.action` attribute on the view, depending on the request method.
            """
            request = super(ViewSetMixin, self).initialize_request(request, *args, **kwargs)
            method = request.method.lower()
            if method == 'options':
                # This is a special case as we always provide handling for the
                # options method in the base `View` class.
                # Unlike the other explicitly defined actions, 'metadata' is implicit.
                self.action = 'metadata'
            else:
                self.action = self.action_map.get(method)
            return request
    
        def reverse_action(self, url_name, *args, **kwargs):
            """
            Reverse the action for the given `url_name`.
            """
            url_name = '%s-%s' % (self.basename, url_name)
            kwargs.setdefault('request', self.request)
    
            return reverse(url_name, *args, **kwargs)
    
        @classmethod
        def get_extra_actions(cls):
            """
            Get the methods that are marked as an extra ViewSet `@action`.
            """
            return [method for _, method in getmembers(cls, _is_extra_action)]
    
        def get_extra_action_url_map(self):
            """
            Build a map of {names: urls} for the extra actions.
    
            This method will noop if `detail` was not provided as a view initkwarg.
            """
            action_urls = OrderedDict()
    
            # exit early if `detail` has not been provided
            if self.detail is None:
                return action_urls
    
            # filter for the relevant extra actions
            actions = [
                action for action in self.get_extra_actions()
                if action.detail == self.detail
            ]
    
            for action in actions:
                try:
                    url_name = '%s-%s' % (self.basename, action.url_name)
                    url = reverse(url_name, self.args, self.kwargs, request=self.request)
                    view = self.__class__(**action.kwargs)
                    action_urls[view.get_view_name()] = url
                except NoReverseMatch:
                    pass  # URL requires additional arguments, ignore
    
            return action_urls
    ViewSetMixin

    (一)as_view

    此时路由中对应的视图执行as_view方法就是ViewSetMixin中的as_view:

        def as_view(cls, actions=None, **initkwargs):
            """
            Because of the way class based views create a closure around the
            instantiated view, we need to totally reimplement `.as_view`,
            and slightly modify the view function that is created and returned.
            """
            # The name and description initkwargs may be explicitly overridden for
            # certain route confiugurations. eg, names of extra actions.
            cls.name = None
            cls.description = None
    
            # The suffix initkwarg is reserved for displaying the viewset type.
            # This initkwarg should have no effect if the name is provided.
            # eg. 'List' or 'Instance'.
            cls.suffix = None
    
            # The detail initkwarg is reserved for introspecting the viewset type.
            cls.detail = None
    
            # Setting a basename allows a view to reverse its action urls. This
            # value is provided by the router through the initkwargs.
            cls.basename = None
    
            # actions must not be empty
            if not actions:
                raise TypeError("The `actions` argument must be provided when "
                                "calling `.as_view()` on a ViewSet. For example "
                                "`.as_view({'get': 'list'})`")
    
            # sanitize keyword arguments
            for key in initkwargs:
                if key in cls.http_method_names:
                    raise TypeError("You tried to pass in the %s method name as a "
                                    "keyword argument to %s(). Don't do that."
                                    % (key, cls.__name__))
                if not hasattr(cls, key):
                    raise TypeError("%s() received an invalid keyword %r" % (
                        cls.__name__, key))
    
            # name and suffix are mutually exclusive
            if 'name' in initkwargs and 'suffix' in initkwargs:
                raise TypeError("%s() received both `name` and `suffix`, which are "
                                "mutually exclusive arguments." % (cls.__name__))
    
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                # We also store the mapping of request methods to actions,
                # so that we can later set the action attribute.
                # eg. `self.action = 'list'` on an incoming GET request.
                self.action_map = actions
    
                # Bind methods to actions
                # This is the bit that's different to a standard view
                for method, action in actions.items():
                    handler = getattr(self, action) #self.list self.create
                    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
    
                # And continue as usual
                return self.dispatch(request, *args, **kwargs)
    
            # take name and docstring from class
            update_wrapper(view, cls, updated=())
    
            # and possible attributes set by decorators
            # like csrf_exempt from dispatch
            update_wrapper(view, cls.dispatch, assigned=())
    
            # We need to set these on the view function, so that breadcrumb
            # generation can pick out these bits of information from a
            # resolved URL.
            view.cls = cls
            view.initkwargs = initkwargs
            view.actions = actions
            return csrf_exempt(view)
    as_view

    在as_view方法中接受参数actions,这就是路由视图as_view传递的参数:

    {"get":"list","post":"create"}

    (二)view

    这个view方法指的是as_view中执行的view方法,它返回的是dispatch方法返回的结果,在view方法中:

            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                # We also store the mapping of request methods to actions,
                # so that we can later set the action attribute.
                # eg. `self.action = 'list'` on an incoming GET request.
                self.action_map = actions
    
                # Bind methods to actions
                # This is the bit that's different to a standard view
                for method, action in actions.items(): #循环actions字典{"get":"list","post":"create"}
                    handler = getattr(self, action) #handleer得到的就是self.list,self.create方法
                    setattr(self, method, handler) #对视图类对象设置属性,getattr(self,method)得到的就是list、create方法
    
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
    
                self.request = request
                self.args = args
                self.kwargs = kwargs
    
                # And continue as usual
                return self.dispatch(request, *args, **kwargs)

      循环actions字典,得到对应的list、create等方法,并且对视图类对象设置属性,每一种请求方式对应不同的action(list、create)而非APIView中的get、post方法,这在dispatch

    方法中体现。

    (三)dispatch

    上述view方法返回的就是dispatch方法返回的结果,而dispatch指的是APIView中的dispatch方法:

      def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
            #rest-framework重构request对象
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
            try:
                self.initial(request, *args, **kwargs)
    
                # Get the appropriate handler method
                #这里和CBV一样进行方法的分发
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                response = handler(request, *args, **kwargs)
    
            except Exception as exc:
                response = self.handle_exception(exc)
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response

    dispatch方法中:

      if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)

    如果此时get请求就会反射执行list方法,这时因为在ViewSetMixin中的as_view方法中的view方法已经setattr(self,method,handler)。

    (四)路由改变

    GenericViewSet类影响的后果就是路由写法的改变,必须传入actions字典

        re_path('books/$', views.BookView.as_view({'get': 'list','post':'create'}), name="books"),
        re_path('books/(?P<pk>d+)/$', views.BookDetailView.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'}), 
          name="booksdetail"),

    实例:

    from rest_framework.viewsets import GenericViewSet
    
    class BookView(GenericViewSet):
    
        queryset = models.Book.objects.all()
        serializer_class = BookModelSerializer
    
        def list(self,request):
            queryset= self.get_queryset()
            bs=self.get_serializer(queryset,many=True)
            return Response(bs.data)

     四、Mixin类

    (一)Mixin类简介

    Mixin 类提供基本视图行为的增、删、查、改操作。注意mixin类提供动作方法,而不是直接定义处理程序方法:

    from typing import Any, Dict
    
    from django.db.models import Model
    from rest_framework.request import Request
    from rest_framework.response import Response
    from rest_framework.serializers import BaseSerializer
    
    class CreateModelMixin(object):
        def create(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
        def perform_create(self, serializer: BaseSerializer) -> None: ...
        def get_success_headers(self, data: Any) -> Dict[str, str]: ...
    
    class ListModelMixin(object):
        def list(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    
    class RetrieveModelMixin(object):
        def retrieve(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    
    class UpdateModelMixin(object):
        def update(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
        def perform_update(self, serializer: BaseSerializer) -> None: ...
        def partial_update(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    
    class DestroyModelMixin(object):
        def destroy(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
        def perform_destroy(self, instance: Model) -> None: ...
    Mixin

      Mixin 类可以从 rest_framework.mixins导入,例如from rest_framework.mixins import 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)

    实例:

    from rest_framework.mixins import ListModelMixin
    from rest_framework.viewsets import GenericViewSet
    
    class BookView(GenericViewSet,ListModelMixin):
        """
        GenericViewSet请求方法与action的对应,也就是路由配置
        ListModelMixin 业务处理
        """
        queryset = models.Book.objects.all()
        serializer_class = BookModelSerializer

    (二)其它功能Mixin类

    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': str(data[api_settings.URL_FIELD_NAME])}
            except (TypeError, KeyError):
                return {}
    CreateModelMixin
    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)
    RetrieveModelMixin
    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)
    UpdateModelMixin
    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()
    DestroyModelMixin

     五、ModelViewSet

    ModelViewSet实现了前面几个类的所有功能,在ModelViewSet中可以看出它继承了前面几个类:

    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        """
        A viewset that provides default `create()`, `retrieve()`, `update()`,
        `partial_update()`, `destroy()` and `list()` actions.
        """
        pass

    ModelViewSet继承了6个类,包含了增、删、查、改以及路由配置类,此时实现功能只需要继承这一个类就足够了。

    实例:

    from rest_framework.viewsets import ModelViewSet
    
    class BookView(ModelViewSet):
    
        queryset = models.Book.objects.all()
        serializer_class = BookModelSerializer

     参考文档:https://www.django-rest-framework.org/api-guide/generic-views/

  • 相关阅读:
    ER模型
    一道人人的笔试题
    关系代数运算
    推荐两个不错的CAD二次开发(.Net)手册
    CAD 致命错误
    CAD二次开发(.NET)之PaletteSet和Palette
    养生
    我看面向对象
    .NET中参数化查询数据库
    自定义按照index和key访问的List
  • 原文地址:https://www.cnblogs.com/shenjianping/p/11490945.html
Copyright © 2020-2023  润新知