コード例 #1
0
ファイル: views.py プロジェクト: sbignell/funkwhale
class TrackViewSet(TagViewSetMixin, viewsets.ReadOnlyModelViewSet):
    """
    A simple ViewSet for viewing and editing accounts.
    """

    queryset = models.Track.objects.all().for_nested_serialization()
    serializer_class = serializers.TrackSerializer
    permission_classes = [common_permissions.ConditionalAuthentication]
    filter_class = filters.TrackFilter
    ordering_fields = (
        "creation_date",
        "title",
        "album__release_date",
        "size",
        "artist__name",
    )

    def get_queryset(self):
        queryset = super().get_queryset()
        filter_favorites = self.request.GET.get("favorites", None)
        user = self.request.user
        if user.is_authenticated and filter_favorites == "true":
            queryset = queryset.filter(track_favorites__user=user)

        queryset = queryset.annotate_playable_by_actor(
            utils.get_actor_from_request(self.request)).annotate_duration()
        if (hasattr(self, "kwargs") and self.kwargs
                and self.request.method.lower() == "get"):
            # we are detailing a single track, so we can add the overhead
            # to fetch additional data
            queryset = queryset.annotate_file_data()
        return queryset.distinct()

    @detail_route(methods=["get"])
    @transaction.non_atomic_requests
    def lyrics(self, request, *args, **kwargs):
        try:
            track = models.Track.objects.get(pk=kwargs["pk"])
        except models.Track.DoesNotExist:
            return Response(status=404)

        work = track.work
        if not work:
            work = track.get_work()

        if not work:
            return Response({"error": "unavailable work "}, status=404)

        lyrics = work.fetch_lyrics()
        try:
            if not lyrics.content:
                tasks.fetch_content(lyrics_id=lyrics.pk)
                lyrics.refresh_from_db()
        except AttributeError:
            return Response({"error": "unavailable lyrics"}, status=404)
        serializer = serializers.LyricsSerializer(lyrics)
        return Response(serializer.data)

    libraries = detail_route(methods=["get"])(get_libraries(
        filter_uploads=lambda o, uploads: uploads.filter(track=o)))
コード例 #2
0
 def decorator(func):
     if detail:
         decorated = detail_route(methods, **kwargs)(func)
     else:
         decorated = list_route(methods, **kwargs)(func)
     decorated.url_path = kwargs.pop('url_path', None) or func.__name__
     decorated.url_name = kwargs.pop('url_name', None) or func.__name__.replace('_', '-')
     decorated.mapping = MethodMapper(func, methods)
     decorated.kwargs = kwargs
     return decorated
コード例 #3
0
ファイル: decorators.py プロジェクト: sassoo/drfjsonapi
def relationship_route(*args, **kwargs):
    """ Convenience decorator to support a JSON API relationship link

    Instead of using a DRF detail_route 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 detail_route(*args, **kwargs)
コード例 #4
0
def async_action(wrapped_action):
    def step(it):
        #TODO check if time is as expected when task is actually launched, issue a warning if it isn't
        try:
            time_delta = next(it)
        except StopIteration:
            return
        tornado.ioloop.IOLoop.instance().add_timeout(timedelta(seconds=time_delta), step, it)

    def wrapper(model, request, *args, **kwargs):
        it = wrapped_action(model, request, *args, **kwargs)
        step(it)
        return Response()

    return detail_route(methods=['post'])(wrapper)
コード例 #5
0
def add_transition_actions(Klass):
    Model = Klass.queryset.model
    fsm_fields = [
        f.name for f in Model._meta.fields if isinstance(f, FSMFieldMixin)
    ]
    if len(fsm_fields) == 0:
        raise ImproperlyConfigured(
            "There is no FSM field at {0}".format(Model))

    if len(fsm_fields) > 1:
        raise ImproperlyConfigured(
            "There is more than one FSM field at {0}".format(Model))

    method_name = "get_all_{0}_transitions".format(fsm_fields[0])

    for f in getattr(Model, method_name)(Model()):

        def get_fn(name_):
            name = copy(name_)

            def fn(self, request, **kwargs):
                args = self.get_serializer(data=request.data)
                args.is_valid(raise_exception=True)
                obj = self.get_object()
                transition = getattr(obj, name)
                if can_proceed(transition) and\
                   has_transition_perm(transition, request.user):
                    try:
                        transition(**args.validated_data)
                    except TransitionNotAllowed as err:
                        raise ValidationError(str(err))
                    obj.save()
                    return Response(status=204)
                else:
                    raise ValidationError(
                        "You cant perform '{}' transition".format(name))

            return fn

        serializer_class = f.custom.get('args_serializer') or Serializer

        setattr(
            Klass, f.name,
            detail_route(methods=['post'],
                         serializer_class=serializer_class)(get_fn(f.name)))
    return Klass
コード例 #6
0
ファイル: views.py プロジェクト: sbignell/funkwhale
class ArtistViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = models.Artist.objects.all()
    serializer_class = serializers.ArtistWithAlbumsSerializer
    permission_classes = [common_permissions.ConditionalAuthentication]
    filter_class = filters.ArtistFilter
    ordering_fields = ("id", "name", "creation_date")

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

    libraries = detail_route(methods=["get"])(
        get_libraries(filter_uploads=lambda o, uploads: uploads.filter(
            Q(track__artist=o) | Q(track__album__artist=o))))
コード例 #7
0
ファイル: decorators.py プロジェクト: sassoo/drfjsonapi
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', ())
    return detail_route(*args, **kwargs)
コード例 #8
0
ファイル: viewsets.py プロジェクト: HadleyLab/drmoonlight_api
    def decorator(Klass):
        Model = Klass.queryset.model
        fsm_fields = [
            f.name for f in Model._meta.fields if isinstance(f, FSMFieldMixin)
        ]
        if len(fsm_fields) == 0:
            raise ImproperlyConfigured(
                "There is no FSM field at '{0}'".format(Model))

        if len(fsm_fields) > 1:
            raise ImproperlyConfigured(
                "There is more than one FSM field at '{0}'".format(Model))

        method_name = "get_all_{0}_transitions".format(fsm_fields[0])

        for f in getattr(Model, method_name)(Model()):
            # Skip methods which explicitly excluded via using `viewset=False`
            # in custom attr
            if not f.custom.get('viewset', True):
                continue

            serializer_class = serializers.get(f.name, None)
            check_args(f, serializer_class)

            if not serializer_class:
                # Use an empty serializer for the transition
                # without data argument
                serializer_class = Serializer

            setattr(
                Klass,
                f.name,
                detail_route(
                    methods=['post'],
                    # Skip permissions because transition has owns
                    permission_classes=(),
                    serializer_class=serializer_class)(get_view_fn(f.name)))
        return Klass
コード例 #9
0
ファイル: views.py プロジェクト: sbignell/funkwhale
class AlbumViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = (models.Album.objects.all().order_by(
        "artist", "release_date").select_related())
    serializer_class = serializers.AlbumSerializer
    permission_classes = [common_permissions.ConditionalAuthentication]
    ordering_fields = ("creation_date", "release_date", "title")
    filter_class = filters.AlbumFilter

    def get_queryset(self):
        queryset = super().get_queryset()
        tracks = models.Track.objects.annotate_playable_by_actor(
            utils.get_actor_from_request(
                self.request)).select_related("artist")
        if (hasattr(self, "kwargs") and self.kwargs
                and self.request.method.lower() == "get"):
            # we are detailing a single album, so we can add the overhead
            # to fetch additional data
            tracks = tracks.annotate_duration()
        qs = queryset.prefetch_related(Prefetch("tracks", queryset=tracks))
        return qs.distinct()

    libraries = detail_route(methods=["get"])(get_libraries(
        filter_uploads=lambda o, uploads: uploads.filter(track__album=o)))
コード例 #10
0
 def link():
     return lambda func: detail_route()(func)
コード例 #11
0
 def action():
     return lambda func: detail_route(methods=['post'])(func)
コード例 #12
0
ファイル: views.py プロジェクト: ctemplin/django-rest-swagger
 def link():
     return lambda func: detail_route()(func)
コード例 #13
0
ファイル: views.py プロジェクト: ctemplin/django-rest-swagger
 def action():
     return lambda func: detail_route(methods=['post'])(func)
コード例 #14
0
ファイル: views.py プロジェクト: paulcwatts/drf-json-schema
    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:
        """Get an object using our faked queryset."""
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
        try:
            return self.get_queryset()[self.kwargs[lookup_url_kwarg]]
コード例 #15
0
ファイル: views.py プロジェクト: deekshabansal5078/djangoo
 def link():
     return lambda func: decorators.detail_route()(func)
コード例 #16
0
class BiobrickViewSet(UserPaginationMixin, BaseBrickViewSet, BrickLookupMixin,
                      viewsets.ReadOnlyModelViewSet):

    renderer_classes = (JSONRenderer, )

    lookup_value_regex = brick_lookup_regex

    def get_object(self):

        if hasattr(self, '_object'):
            return self._object

        self._object = self.get_brick_object()
        return self._object

    def parse_query(self):

        queryset = SearchQuerySet()
        statements = self.request.query_params.get('q', '').split()
        keywords = []
        types = []
        names = []
        orderings = []
        authors = []
        highlight = False

        for statement in statements:
            if statement.startswith('o:'):
                orderings.append(statement[2:])
            if statement.startswith('n:'):
                names.append(statement[2:])
            elif statement.startswith('t:'):
                types.append(statement[2:])
            elif statement.startswith('h:'):
                highlight = True
            elif statement.startswith('a:'):
                authors.append(statement[2:])
            else:
                keywords.append(statement)

        condition = None

        for field, items in (('text', keywords), ('part_name', names),
                             ('part_type', types), ('author', authors)):
            if items:
                clause = reduce(
                    or_, map(lambda kw: SQ(**{field + '__contains': kw}),
                             items))
                if condition is not None:
                    condition &= clause
                else:
                    condition = clause

        if condition is not None:
            queryset = queryset.filter(condition)

        if not orderings:
            orderings = ['-weight', '-creation_date']

        orderings = unique(orderings)

        queryset = queryset.order_by(*orderings)

        if highlight:
            queryset = queryset.highlight(
                pre_tags=['<span class="highlight">'], post_tags=['</span>'])

        return queryset

    def list(self, request, *args, **kwargs):
        context = OrderedDict()

        queryset = self.parse_query()
        queryset.load_all()

        page = self.paginate_queryset(queryset.order_by('-weight'))
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            response = self.get_paginated_response(serializer.data)
        else:
            serializer = self.get_serializer(queryset, many=True)
            response = Response(serializer.data)
        response.data.update(context)
        return response

    @list_route(methods=['GET'])
    def popular(self, request, *args, **kwargs):
        try:
            size = int(request.query_params.get('size', 4))
        except ValueError:
            size = 4

        return Response(brick_views_manager.get_by_random(size))

    @detail_route(methods=['GET'])
    def stats(self, request, *args, **kwargs):

        user = request.user

        if not user.is_authenticated():
            return Response({})

        try:
            brick_meta = BiobrickMeta.objects.only('part_name').get(
                **self.get_brick_lookup_options())
        except BiobrickMeta.DoesNotExist:
            return Response({})

        result = {}
        for name, model in (('watched', WatchingUser), ('rated', RatedUser),
                            ('starred', StarredUser)):
            try:
                obj = model.objects.get(user=user, brick=brick_meta)
                result[name] = True

                if name == 'rated':
                    result['score'] = obj.score
            except model.DoesNotExist:
                result[name] = False

        return Response(result)

    def retrieve(self, request, *args, **kwargs):

        brick = self.get_object()
        brick_views_manager.handle_request(
            request, brick.part_name)  # Process views count

        if not request.query_params.get('nofetch', '') and brick.should_fetch:
            try:
                brick.fetch()
            except SpiderError as e:
                logging.error(str(e))

        serializer = BiobrickSerializer(brick, context=dict(request=request))
        return Response(serializer.data)

    # User-Biobrick m2m retrieving

    for view_name, attribute in (('users_watching', ) * 2,
                                 ('users_rated', ) * 2,
                                 ('users_starred', ) * 2):
        locals()[view_name] = detail_route(methods=['GET'])(
            # use closure to generate a scope
            (
                lambda attribute:
                # view
                lambda self, *args, **kwargs: self.paginate_user_queryset(
                    getattr(self.get_object(), attribute).all()))(attribute))

    def assert_brick_action(self, action, success='OK', fail='Fail'):
        """
        This is a helper function to reduce code duplication.

        `action` should be a callable accepting a Brick object and returning a
        bool.
        `success` and `fail` are optional response payload.
        """
        if action(self.get_object()):
            return Response(success)
        else:
            raise ValidationError(fail)

    # Extra actions

    for view_name, action in (('watch',
                               lambda self, b: b.watch(self.request.user)),
                              ('unwatch',
                               lambda self, b: b.unwatch(self.request.user)),
                              ('star',
                               lambda self, b: b.star(self.request.user)),
                              ('unstar',
                               lambda self, b: b.unstar(self.request.user))):
        locals()[view_name] = detail_route(
            methods=['POST'],
            permission_classes=(permissions.IsAuthenticated, ))(
                # use closure to generate a scope
                (
                    lambda action:
                    # view
                    lambda self, *args, **kwargs: self.assert_brick_action(
                        lambda b: action(self, b)))(action))

    @detail_route(methods=['POST'],
                  permission_classes=(permissions.IsAuthenticated, ))
    def rate(self, request, *args, **kwargs):
        serializer = RateSerializer(data=request.data,
                                    context={
                                        'user': request.user,
                                        'brick': self.get_object()
                                    })

        if serializer.is_valid(raise_exception=True):
            result = serializer.save()
            return self.assert_brick_action(lambda b: result)

    @detail_route(methods=['GET'])
    def related(self, request, *args, **kwargs):
        part_name = self.get_brick_lookup_options()['part_name']

        return Response(brick_getter.get_related_bricks(part_name))
コード例 #17
0
 def action(detail=True, methods=None):
     return detail_route(methods)