예제 #1
0
class ApiViewSet(viewsets.ViewSet):
    def list(self, request):
        return Response("Este endpoint utiliza o verbo GET.")

    def create(self, request):
        return Response(f"Este endpoint utiliza o verbo POST.")

    action(detail=False, methods=['put'])

    def put(self, request):
        return Response("Este endpoint utiliza o verbo PUT.")

    action(detail=False, methods=['post'])

    def post_dado(self, request, dado):
        request.data['dado']
        return Response(f"Este endpoint recebe o nome {dado} via URL.")

    action(detail=False, methods=['put'])

    def put_dado(self, request, *args, **kwargs):
        return Response(f"Este endpoint recebe o nome {dado} via URL.")

    action(detail=False, methods=['delete'])

    def delete(self, request):
        return Response(f"Este endpoint utiliza o verbo DELETE.")
예제 #2
0
def project_action(viewset_method=None,
                   serializer_class=serializers.Serializer):
    """
    Decorator that turns a project viewset method into an action that receives
    the project as its only argument.
    """
    # If no viewset method is given, return a decorator function
    if viewset_method is None:
        return partial(project_action, serializer_class=serializer_class)
    # Define the wrapper that processes the incoming data and finds the project
    @wraps(viewset_method)
    def wrapper(viewset, request, pk=None):
        # Get the project first - this will also process permissions
        project = viewset.get_object()
        # Then process the input data according to the serializer
        serializer = viewset.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # Then call the view function
        return viewset_method(viewset, project, serializer.data)

    # Wrap the viewset method in the action decorator
    action_decorator = action(detail=True,
                              methods=['POST'],
                              serializer_class=serializer_class)
    return action_decorator(wrapper)
예제 #3
0
def fetches_route():
    @transaction.atomic
    def fetches(self, request, *args, **kwargs):
        obj = self.get_object()
        if request.method == "GET":
            queryset = models.Fetch.objects.get_for_object(obj).select_related(
                "actor")
            queryset = queryset.order_by("-creation_date")
            filterset = filters.FetchFilter(request.GET, queryset=queryset)
            page = self.paginate_queryset(filterset.qs)
            if page is not None:
                serializer = api_serializers.FetchSerializer(page, many=True)
                return self.get_paginated_response(serializer.data)

            serializer = api_serializers.FetchSerializer(queryset, many=True)
            return response.Response(serializer.data)
        if request.method == "POST":
            if utils.is_local(obj.fid):
                return response.Response(
                    {"detail": "Cannot fetch a local object"}, status=400)

            fetch = models.Fetch.objects.create(url=obj.fid,
                                                actor=request.user.actor,
                                                object=obj)
            common_utils.on_commit(tasks.fetch.delay, fetch_id=fetch.pk)
            serializer = api_serializers.FetchSerializer(fetch)
            return response.Response(serializer.data,
                                     status=status.HTTP_201_CREATED)

    return decorators.action(
        methods=["get", "post"],
        detail=True,
        permission_classes=[permissions.IsAuthenticated],
    )(fetches)
예제 #4
0
class ActorViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    queryset = models.Actor.objects.select_related("user", "channel",
                                                   "summary_obj",
                                                   "attachment_icon")
    permission_classes = [ConditionalAuthentication]
    serializer_class = api_serializers.FullActorSerializer
    lookup_field = "full_username"
    lookup_value_regex = r"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)"

    def get_object(self):
        queryset = self.get_queryset()
        username, domain = self.kwargs["full_username"].split("@", 1)
        return queryset.get(preferred_username=username, domain_id=domain)

    def get_queryset(self):
        qs = super().get_queryset()
        qs = qs.exclude(
            domain__instance_policy__is_active=True,
            domain__instance_policy__block_all=True,
        )
        if preferences.get("moderation__allow_list_enabled"):
            query = Q(domain_id=settings.FUNKWHALE_HOSTNAME) | Q(
                domain__allowed=True)
            qs = qs.filter(query)
        return qs

    libraries = decorators.action(methods=[
        "get"
    ], detail=True)(music_views.get_libraries(
        filter_uploads=lambda o, uploads: uploads.filter(library__actor=o)))
예제 #5
0
    def decorator(func: _t.Callable):
        # pylint: disable=used-before-assignment
        def wrapper(view: NestedViewMixin, request: drf_request.Request, *args,
                    **kwargs):
            # Nested name
            view.nested_name = name
            # Allow append to nested or only create
            view.nested_allow_append = allow_append
            # ID name of nested object
            view.nested_arg = request_arg
            view.nested_append_arg = append_arg
            view.nested_id = kwargs.get(view.nested_arg, None)
            view.nested_view_object = None
            view._nested_filter_class = _nested_filter_class
            return func(view, request, *args)

        wrapper.__name__ = func.__name__
        kwargs['methods'] = methods
        kwargs['detail'] = True
        kwargs['url_path'] = path
        kwargs['url_name'] = kwargs.pop('url_name', name)
        view: NestedViewMixin = action(*args,
                                       **kwargs)(wrapper)  # type: ignore
        view._nested_args = _nested_args
        view._nested_manager = manager_name or name
        view._nested_filter_class = _nested_filter_class
        if arg:
            view._nested_args[name] = request_arg
        return view
예제 #6
0
    def decorator(func):
        def wrapper(view, request, *args, **kwargs):
            # Nested name
            view.nested_name = name
            # Nested parent object
            view.nested_parent_object = view.get_object()
            # Allow append to nested or only create
            view.nested_allow_append = allow_append
            # ID name of nested object
            view.nested_arg = request_arg
            view.nested_append_arg = append_arg
            view.nested_id = kwargs.get(view.nested_arg, None)
            view.nested_manager = getattr(
                view.nested_parent_object, manager_name or name, None
            )
            view.nested_view_object = None
            view._nested_filter_class = _nested_filter_class
            return func(view, request, *args)

        wrapper.__name__ = func.__name__
        kwargs['methods'] = methods
        kwargs['detail'] = True
        kwargs['url_path'] = path
        kwargs['url_name'] = kwargs.pop('url_name', name)
        view = action(*args, **kwargs)(wrapper)
        view._nested_args = _nested_args
        view._nested_manager = manager_name or name
        view._nested_filter_class = _nested_filter_class
        if arg:
            view._nested_args[name] = request_arg
        return view
예제 #7
0
    def decorator(func):
        func = action(methods=[method], detail=True, **kwargs)(func)
        func.action_type = 'custom'
        func.action_kwargs = action_kwargs(icon_class, btn_class, text, func,
                                           kwargs)
        func.kwargs = {}

        return func
예제 #8
0
    def decorator(func):
        func = action(methods=[method], detail=False, **kwargs)(func)
        func.action_type = 'bulk'
        func.action_kwargs = action_kwargs(icon_class, btn_class, text, func,
                                           kwargs)
        func.action_kwargs['atOnce'] = func.action_kwargs.get('atOnce', True)
        func.kwargs = {}

        return func
예제 #9
0
 def decorator(func):
     func_object = action(*args, **kwargs)(func)
     override_kw = dict()
     if response_code:
         override_kw['responses'] = {response_code: response_serializer()}
     if operation_description:
         override_kw['operation_description'] = operation_description
     else:
         override_kw['operation_description'] = str(func.__doc__
                                                    or '').strip()
     return swagger_auto_schema(**override_kw)(func_object)
예제 #10
0
    def decorator(func):
        if action is not None:
            func = action(methods=[method], detail=True, **kwargs)(func)
        else:
            func.detail = True
            func.bind_to_methods = [method]
        func.action_type = 'custom'
        func.action_kwargs = action_kwargs(icon_class, btn_class, text, func, kwargs)
        func.kwargs = {}

        return func
예제 #11
0
    def test_view_name_kwargs(self):
        """
        'name' and 'suffix' are mutually exclusive kwargs used for generating
        a view's display name.
        """
        # by default, generate name from method
        @action(detail=True)
        def test_action(request):
            raise NotImplementedError

        assert test_action.kwargs == {
            'description': None,
            'name': 'Test action',
        }

        # name kwarg supersedes name generation
        @action(detail=True, name='test name')
        def test_action(request):
            raise NotImplementedError

        assert test_action.kwargs == {
            'description': None,
            'name': 'test name',
        }

        # suffix kwarg supersedes name generation
        @action(detail=True, suffix='Suffix')
        def test_action(request):
            raise NotImplementedError

        assert test_action.kwargs == {
            'description': None,
            'suffix': 'Suffix',
        }

        # name + suffix is a conflict.
        with pytest.raises(TypeError) as excinfo:
            action(detail=True, name='test name', suffix='Suffix')

        assert str(excinfo.value
                   ) == "`name` and `suffix` are mutually exclusive arguments."
예제 #12
0
    def get_extra_actions(cls):
        extra_actions = super().get_extra_actions()  # type: ignore

        if cls.time_bucket_serializer_class is not None:
            extra_actions.append(
                action(
                    detail=False,
                    methods=["get"],
                    serializer_class=cls.time_bucket_serializer_class,
                )(cls.stats))

        return extra_actions
예제 #13
0
    def decorator(func):
        if action is not None:
            func = action(methods=[method], detail=False, **kwargs)(func)
        else:
            func.bind_to_methods = [method, ]
            func.detail = False
        func.action_type = 'bulk'
        func.action_kwargs = action_kwargs(icon_class, btn_class, text, func, kwargs)
        func.action_kwargs['atOnce'] = func.action_kwargs.get('atOnce', True)
        func.kwargs = {}

        return func
    def test_view_name_kwargs(self):
        """
        'name' and 'suffix' are mutually exclusive kwargs used for generating
        a view's display name.
        """
        # by default, generate name from method
        @action(detail=True)
        def test_action(request):
            raise NotImplementedError

        assert test_action.kwargs == {
            'description': None,
            'name': 'Test action',
        }

        # name kwarg supersedes name generation
        @action(detail=True, name='test name')
        def test_action(request):
            raise NotImplementedError

        assert test_action.kwargs == {
            'description': None,
            'name': 'test name',
        }

        # suffix kwarg supersedes name generation
        @action(detail=True, suffix='Suffix')
        def test_action(request):
            raise NotImplementedError

        assert test_action.kwargs == {
            'description': None,
            'suffix': 'Suffix',
        }

        # name + suffix is a conflict.
        with pytest.raises(TypeError) as excinfo:
            action(detail=True, name='test name', suffix='Suffix')

        assert str(excinfo.value) == "`name` and `suffix` are mutually exclusive arguments."
예제 #15
0
    def __new__(cls, name, bases, dct):
        _class = super().__new__(cls, name, bases, dct)

        # The class needs the field FSM_MODELFIELDS to know which transitions it needs to add
        if hasattr(_class, "get_model"):
            model = _class.get_model()

            if model:
                setattr(_class, "FSM_BUTTONS",
                        getattr(_class, "FSM_BUTTONS", set()))
                # The model potentially has multiple FSMFields, which needs to be iterated over
                for field in filter(lambda f: isinstance(f, FSMField),
                                    model._meta.fields):
                    # Get all transitions, by calling the partialmethod defined by django-fsm
                    transitions = getattr(
                        model, f"get_all_{field.name}_transitions")(model())

                    # Since the method above can potentially return a transition multiple times
                    # i.e. when a transitions has multiple sources, we need to filter out those transitions
                    _discovered_transitions = list()

                    for transition in transitions:
                        if transition.name in _discovered_transitions:
                            continue
                        else:
                            _discovered_transitions.append(transition.name)

                        # Get the Transition Button and add it to the front of the instance buttons
                        button = transition.custom.get("_transition_button")
                        _class.FSM_BUTTONS.add(button)

                        # Create a method that calls fsm_route with the request and the action name
                        method = get_method(transition)

                        # We need to manually change the method name, otherwise django-fsm won't
                        # Add this method to the URLs

                        # Wrap the above defined method in the action decorator
                        # IMPORTANT: This needs to happen after we changed the method name
                        # therefore we cannot use the proper decorator
                        method.__name__ = transition.name
                        method.__doc__ = transition.method.__doc__

                        wrapped_method = action(detail=True,
                                                methods=["GET",
                                                         "PATCH"])(method)

                        # Set the method as a attribute of the class that implements this
                        # metaclass
                        setattr(_class, transition.name, wrapped_method)

        return _class
예제 #16
0
def action(
    methods: List[str],
    *,
    detail: bool,
    ordering: Optional[str] = None,
    ordering_fields: Optional[List[str]] = None,
    filterset_class: Optional[Type[FilterSet]] = None,
    **kwargs: Any,
):
    """
    Custom action decorator that wraps rest_framework.decorators.action. Use
    this decorator to reset view class configurations e.g. serializer_class,
    ordering, etc.

    :param methods: List of http methods e.g. ["get", "post"]
    :param detail: Boolean:
        Switch between a detail route (True) and a list route (False).
    :param ordering: Optional list of strings
        Default ordering on the queryset. Default: None
    :param ordering_fields: Optional list of strings.
        List options for ordering. Default: None
    :param filterset_class: Option FilterSet class. Default: None
    :param kwargs: Optional keyword arguments
        e.g. serializer_class, filter_backends, etc.
    :return: Called rest_framework.decorators.action

    Usage:

    from drft.views import action

    class SomeViewSet(...):
        @action(["POST", "GET"], detail=False)
        def some_view(self, request: Request) -> Response:
            ...

        @action(["POST", "GET"], detail=True)
        def some_other_view(
            self,
            request: Request,
            pk: Optional[str] = None
        ) -> Response:
            ...
    """
    defaults = dict(
        detail=detail,
        ordering=ordering,
        ordering_fields=ordering_fields,
        filterset_class=filterset_class,
    )
    defaults.update(kwargs)
    return decorators.action(methods, **defaults)
예제 #17
0
def relationship_route(*args, **kwargs):
    """ Convenience decorator to support a JSON API relationship link

    Instead of using a DRF action decorator when constructing
    views that would resolve "Relationship Links" according
    to the JSON API spec, you should use this decorator.

    :spec:
        jsonapi.org/format/#fetching-relationships
    """

    kwargs['filter_backends'] = kwargs.pop('filter_backends', ())
    kwargs['url_path'] = 'relationships/%s' % kwargs.pop('relation')
    return action(*args, detail=True, **kwargs)
예제 #18
0
파일: views.py 프로젝트: mayhem/funkwhale
class ArtistViewSet(
        HandleInvalidSearch,
        common_views.SkipFilterForGetObject,
        viewsets.ReadOnlyModelViewSet,
):
    queryset = (models.Artist.objects.all().prefetch_related(
        "attributed_to", "attachment_cover").prefetch_related(
            "channel__actor",
            Prefetch(
                "tracks",
                queryset=models.Track.objects.all(),
                to_attr="_prefetched_tracks",
            ),
        ).order_by("-id"))
    serializer_class = serializers.ArtistWithAlbumsSerializer
    permission_classes = [oauth_permissions.ScopePermission]
    required_scope = "libraries"
    anonymous_policy = "setting"
    filterset_class = filters.ArtistFilter

    fetches = federation_decorators.fetches_route()
    mutations = common_decorators.mutations_route(types=["update"])

    def get_object(self):
        obj = super().get_object()

        if (self.action == "retrieve"
                and self.request.GET.get("refresh", "").lower() == "true"):
            obj = refetch_obj(obj, self.get_queryset())
        return obj

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context["description"] = self.action in [
            "retrieve", "create", "update"
        ]
        return context

    def get_queryset(self):
        queryset = super().get_queryset()
        albums = (models.Album.objects.with_tracks_count().select_related(
            "attachment_cover").prefetch_related("tracks"))
        albums = albums.annotate_playable_by_actor(
            utils.get_actor_from_request(self.request))
        return queryset.prefetch_related(Prefetch("albums", queryset=albums),
                                         TAG_PREFETCH)

    libraries = action(methods=["get"], detail=True)(
        get_libraries(filter_uploads=lambda o, uploads: uploads.filter(
            Q(track__artist=o) | Q(track__album__artist=o))))
예제 #19
0
def _state_change_action(name, state: Thesis.State):
    def _action_method(self: 'ThesisViewSet', request: Request, *args, **kwargs):
        thesis = self.get_object()  # type: Thesis
        self.get_serializer()
        serializer = self.get_serializer(instance=thesis, data=dict(state=state), partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(data=serializer.data)

    _action_method.__name__ = name

    return transaction.atomic(
        action(methods=['patch'], detail=True)(_action_method)
    )
예제 #20
0
    def as_interface(
        cls, url_name=None, url_path=None, methods=None, **kwargs
    ):
        """
        Creates a DRF action from a the interface.

        Args:
            url_name (str, default=cls.url_name): The url_name
                argument that is passed to the DRF @action decorator.
            url_path (str, default=cls.url_path): The url_path
                argument that is passed to the DRF @action decorator.
            methods (list, default=[POST]): The list of methods over
                which the action will be available.
            **kwargs: Any additional argument accepted by the drf.action
                decorator.
        """
        # NOTE(@tomage): Moving this import in here, as if it is on module top-
        # level, it results in an error if `daf.rest_framework` is imported
        # prematurely in another process (e.g. before Django has loaded up the
        # settings module).
        # It is generally discouraged that libraries do this (see django docs)
        # and this issue has been reported to DRF in particular (see
        # here: https://github.com/encode/django-rest-framework/issues/6030).
        import rest_framework.decorators as drf_decorators

        def _drf_detail_action(viewset, request, pk, **kwargs):
            """
            The code that is executed in the DRF viewset
            """
            return cls(viewset, request, pk).run()

        url_name = url_name or cls.url_name
        url_path = url_path or cls.url_path
        methods = methods or cls.methods or ['post']

        func = _drf_detail_action
        func.__name__ = 'detail_' + cls.action.name
        func.__doc__ = cls.__doc__

        return drf_decorators.action(
            methods=methods,
            detail=True,
            url_path=url_path,
            url_name=url_name,
            **kwargs,
        )(func)
예제 #21
0
    def decorator(func):
        def wizard_func(self, request, *args, **kwargs):
            Serializer = serializer
            serializer_instance = Serializer(data=request.data)

            if not serializer_instance.is_valid():
                return Response(serializer_instance.errors, status=400)

            request.validated_data = serializer_instance.validated_data

            return func(self, request, *args, **kwargs)

        wizard_func.__name__ = func.__name__
        if meta_type == 'custom':
            detail = True
        else:
            detail = False

        if action is not None:
            wizard_func = action(methods=[kwargs.pop('method', 'post')],
                                 detail=detail,
                                 **kwargs)(wizard_func)  # NoQA
            wizard_func.__name__ = func.__name__
        else:
            wizard_func.bind_to_methods = [
                kwargs.pop('method', 'POST'),
            ]
            wizard_func.detail = detail

        wizard_func.action_type = meta_type
        wizard_func.wizard = True
        wizard_func.action_kwargs = action_kwargs(icon_class, btn_class, text,
                                                  wizard_func, kwargs)
        wizard_func.kwargs = {}
        if target_model is not None:
            wizard_func.action_kwargs['params']['model'] = '{}/{}/{}'.format(
                target_model._meta.app_label.lower().replace('_', '-'),
                inflector.pluralize(target_model._meta.model_name.lower()),
                wizard_func.__name__)
        wizard_func.serializer = serializer

        return Adapter.adapt_wizard(wizard_func)
예제 #22
0
    def decorator(func: _t.Callable):
        func_object = action(*args, **kwargs)(func)
        override_kw: _t.Dict = {
            'methods': tuple(func_object.mapping.keys()) or None
        }  # type: ignore

        if response_code:
            override_kw['responses'] = {response_code: response_serializer()}

        if operation_description:
            override_kw['operation_description'] = operation_description
        else:
            override_kw['operation_description'] = str(
                func.__doc__ or '').strip()  # type: ignore

        override_kw['x-multiaction'] = bool(is_mul)

        override_kw['x-require-confirmation'] = bool(require_confirmation)

        return swagger_auto_schema(**override_kw)(func_object)  # type: ignore
예제 #23
0
    def build_form(clz, form_name, form_def):
        def form_method(self, request, pk, *args, **kwargs):
            viewset = form_def["viewset"]()
            viewset.request = request
            viewset.format_kwarg = self.format_kwarg
            link = form_def["link"]
            if isinstance(link, str):
                serializer = viewset.get_serializer()
                fld = serializer.fields[link]
                if isinstance(fld, PrimaryKeyRelatedField):
                    request.data[link] = self.get_object().pk
                else:
                    request.data[link] = self.get_serializer(
                        instance=self.get_object()).data

            method = form_def["method"]
            if "link_id" in form_def:
                link_id = request.GET.get(
                    form_def["link_id"]) or request.data.get(
                        form_def["link_id"])
                viewset.lookup_url_kwarg = form_def["link_id"]
                viewset.kwargs = {viewset.lookup_url_kwarg: link_id}
            else:
                link_id = None

            if request._request.method == "GET":
                method = "retrieve"
            elif not method:
                method = "create" if not link_id else "update"

            return getattr(viewset, method)(request, *args, **kwargs)

        form_method.__name__ = form_name.replace("-", "_")

        return (
            form_name.replace("-", "_"),
            action(methods=["get", "post", "patch"],
                   detail=True,
                   url_path=form_name)(form_method),
        )
def get_drf_fsm_mixin(Model, fieldname='state'):
    """
    Find all transitions defined on `Model`, then create a corresponding
    viewset action method for each and apply it to `Mixin`. Finally, return
    `Mixin`
    """
    class Mixin(object):
        save_after_transition = True

        @action(methods=['GET'],
                detail=True,
                url_name='possible-transitions',
                url_path='possible-transitions')
        def possible_transitions(self, request, *args, **kwargs):
            instance = self.get_object()
            return Response(
                {
                    'transitions': [
                        trans.name.replace('_', '-') for trans in getattr(
                            instance, 'get_available_{}_transitions'.format(
                                fieldname))()
                        if trans.has_perm(instance, request.user)
                    ]
                }, )

    transitions = getattr(Model(),
                          'get_all_{}_transitions'.format(fieldname))()
    transition_names = set(x.name for x in transitions)

    for transition_name in transition_names:
        url = transition_name.replace('_', '-')
        setattr(
            Mixin,
            transition_name,
            action(methods=['POST'], detail=True, url_name=url, url_path=url)(
                get_transition_viewset_method(transition_name)),
        )

    return Mixin
    def as_interface(cls,
                     url_name=None,
                     url_path=None,
                     methods=None,
                     **kwargs):
        """
        Creates a DRF action from a the interface.

        Args:
            url_name (str, default=cls.url_name): The url_name
                argument that is passed to the DRF @action decorator.
            url_path (str, default=cls.url_path): The url_path
                argument that is passed to the DRF @action decorator.
            methods (list, default=[POST]): The list of methods over
                which the action will be available.
            **kwargs: Any additional argument accepted by the drf.action
                decorator.
        """
        def _drf_detail_action(viewset, request, pk, **kwargs):
            """
            The code that is executed in the DRF viewset
            """
            return cls(viewset, request, pk).run()

        url_name = url_name or cls.url_name
        url_path = url_path or cls.url_path
        methods = methods or cls.methods or ['post']

        func = _drf_detail_action
        func.__name__ = 'detail_' + cls.action.name
        func.__doc__ = cls.__doc__

        return drf_decorators.action(
            methods=methods,
            detail=True,
            url_path=url_path,
            url_name=url_name,
            **kwargs,
        )(func)
    def build_form(clz, form_name, form_def):
        def form_method(self, request, pk, *args, **kwargs):
            viewset = form_def['viewset']()
            viewset.request = request
            viewset.format_kwarg = self.format_kwarg
            link = form_def['link']
            if isinstance(link, str):
                serializer = viewset.get_serializer()
                fld = serializer.fields[link]
                if isinstance(fld, PrimaryKeyRelatedField):
                    request.data[link] = self.get_object().pk
                else:
                    request.data[link] = self.get_serializer(
                        instance=self.get_object()).data

            method = form_def['method']
            if 'link_id' in form_def:
                link_id = request.GET.get(
                    form_def['link_id']) or request.data.get(
                        form_def['link_id'])
                viewset.lookup_url_kwarg = form_def['link_id']
                viewset.kwargs = {viewset.lookup_url_kwarg: link_id}
            else:
                link_id = None

            if request._request.method == 'GET':
                method = 'retrieve'
            elif not method:
                method = 'create' if not link_id else 'update'

            return getattr(viewset, method)(request, *args, **kwargs)

        form_method.__name__ = form_name.replace('-', '_')

        return (form_name.replace('-', '_'),
                action(methods=['get', 'post', 'patch'],
                       detail=True,
                       url_path=form_name)(form_method))
예제 #27
0
def related_route(*args, **kwargs):
    """ Convenience decorator to support JSON API related resource link

    Instead of using a DRF detail_route decorator when constructing
    views that would resolve "Related Resource Links" according
    to the JSON API spec, you should use this decorator.

    It simply wraps the detail_route but skips the processing
    of filter_backends on the primary data since all query
    params provided are for the RELATED data & not the primary
    data.

    For example, `/actors/1/movies?sort=title` would ensure
    the sort query param would be processed on the movies
    resource & NOT the actors resource.

    :spec:
        jsonapi.org/format/#document-resource-object-related-resource-links
    """

    kwargs['filter_backends'] = kwargs.pop('filter_backends', ())
    kwargs['url_name'] = kwargs.get('url_name', kwargs['url_path'])
    return action(*args, detail=True, **kwargs)
예제 #28
0
 def detail_route(methods, suffix, url_path=None):
     return action(detail=True, methods=methods, suffix=suffix)
예제 #29
0
    NonDefaultIdSerializer,
    get_artists,
    get_albums,
    get_tracks,
    get_non_default_ids,
    QuerySet,
    Artist,
    Album,
    Track,
    NonDefaultId,
)

try:
    from rest_framework.decorators import action

    action_route = action(detail=True, methods=["get"])
except ImportError:
    from rest_framework.decorators import detail_route

    action_route = detail_route(methods=["get"])


class BaseViewSet(viewsets.ModelViewSet):
    """Base view set for our "models"."""

    parser_classes = (JSONAPIParser, )
    permission_classes = (AllowAny, )
    renderer_classes = (JSONAPIRenderer, )
    content_negotiation_class = JSONAPIContentNegotiation

    def get_object(self) -> Any:
예제 #30
0
 def detail_route(methods, suffix):
     return action(detail=True, methods=methods, suffix=suffix)
예제 #31
0
 def list_route(methods=None, **kwargs):
     return action(detail=False, **kwargs)