123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- # Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
- # Copyright (C) 2014 Jesús Espino <jespinog@gmail.com>
- # Copyright (C) 2014 David Barragán <bameda@dbarragan.com>
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Affero General Public License as
- # published by the Free Software Foundation, either version 3 of the
- # License, or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU Affero General Public License for more details.
- #
- # You should have received a copy of the GNU Affero General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- # This code is partially taken from django-rest-framework:
- # Copyright (c) 2011-2014, Tom Christie
- from functools import update_wrapper
- from django.utils.decorators import classonlymethod
- from django.utils.translation import ugettext as _
- from . import views
- from . import mixins
- from . import generics
- 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 suffix initkwarg is reserved for identifing the viewset type
- # eg. 'List' or 'Instance'.
- cls.suffix = None
- # 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))
- 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)
- setattr(self, method, handler)
- # Patch this in as it's otherwise only present from 1.5 onwards
- if hasattr(self, 'get') and not hasattr(self, 'head'):
- self.head = self.get
- # 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.suffix = initkwargs.get('suffix', None)
- return view
- def initialize_request(self, request, *args, **kargs):
- """
- Set the `.action` attribute on the view,
- depending on the request method.
- """
- request = super(ViewSetMixin, self).initialize_request(request, *args, **kargs)
- self.action = self.action_map.get(request.method.lower())
- return request
- def check_permissions(self, request, action:str=None, obj:object=None):
- if action is None:
- action = self.action
- return super().check_permissions(request, action=action, obj=obj)
- class ViewSet(ViewSetMixin, views.APIView):
- """
- The base ViewSet class does not provide any actions by default.
- """
- pass
- 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
- class ReadOnlyListViewSet(GenericViewSet):
- """
- A viewset that provides default `list()` action.
- """
- pass
- class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
- mixins.ListModelMixin,
- GenericViewSet):
- """
- A viewset that provides default `list()` and `retrieve()` actions.
- """
- pass
- 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
- class ModelCrudViewSet(ModelViewSet):
- pass
- class ModelListViewSet(mixins.RetrieveModelMixin,
- mixins.ListModelMixin,
- GenericViewSet):
- pass
- class ModelUpdateRetrieveViewSet(mixins.UpdateModelMixin,
- mixins.RetrieveModelMixin,
- GenericViewSet):
- pass
|