Verified Commit 14392ebb authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Updated rest framework to 3.9

parent 4a6df063
......@@ -10,7 +10,7 @@ from funkwhale_api.playlists import views as playlists_views
from funkwhale_api.subsonic.views import SubsonicViewSet
router = routers.SimpleRouter()
router.register(r"settings", GlobalPreferencesViewSet, base_name="settings")
router.register(r"settings", GlobalPreferencesViewSet, basename="settings")
router.register(r"activity", activity_views.ActivityViewSet, "activity")
router.register(r"tags", views.TagViewSet, "tags")
router.register(r"tracks", views.TrackViewSet, "tracks")
......@@ -27,7 +27,7 @@ router.register(
v1_patterns = router.urls
subsonic_router = routers.SimpleRouter(trailing_slash=False)
subsonic_router.register(r"subsonic/rest", SubsonicViewSet, base_name="subsonic")
subsonic_router.register(r"subsonic/rest", SubsonicViewSet, basename="subsonic")
v1_patterns += [
......
from rest_framework import response
from rest_framework.decorators import list_route
from rest_framework import decorators
def action_route(serializer_class):
@list_route(methods=["post"])
@decorators.action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = serializer_class(request.data, queryset=queryset)
......
from rest_framework import mixins, status, viewsets
from rest_framework.decorators import list_route
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.response import Response
......@@ -62,7 +62,7 @@ class TrackFavoriteViewSet(
favorite = models.TrackFavorite.add(track=track, user=self.request.user)
return favorite
@list_route(methods=["delete", "post"])
@action(methods=["delete", "post"], detail=False)
def remove(self, request, *args, **kwargs):
try:
pk = int(request.data["track"])
......@@ -72,7 +72,7 @@ class TrackFavoriteViewSet(
favorite.delete()
return Response([], status=status.HTTP_204_NO_CONTENT)
@list_route(methods=["get"])
@action(methods=["get"], detail=False)
def all(self, request, *args, **kwargs):
"""
Return all the favorites of the current user, with only limited data
......
......@@ -66,7 +66,7 @@ class LibraryFollowViewSet(
context["actor"] = self.request.user.actor
return context
@decorators.detail_route(methods=["post"])
@decorators.action(methods=["post"], detail=True)
def accept(self, request, *args, **kwargs):
try:
follow = self.queryset.get(
......@@ -77,7 +77,7 @@ class LibraryFollowViewSet(
update_follow(follow, approved=True)
return response.Response(status=204)
@decorators.detail_route(methods=["post"])
@decorators.action(methods=["post"], detail=True)
def reject(self, request, *args, **kwargs):
try:
follow = self.queryset.get(
......@@ -105,7 +105,7 @@ class LibraryViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
qs = super().get_queryset()
return qs.viewable_by(actor=self.request.user.actor)
@decorators.detail_route(methods=["post"])
@decorators.action(methods=["post"], detail=True)
def scan(self, request, *args, **kwargs):
library = self.get_object()
if library.actor.get_user():
......@@ -122,7 +122,7 @@ class LibraryViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
)
return response.Response({"status": "skipped"}, 200)
@decorators.list_route(methods=["post"])
@decorators.action(methods=["post"], detail=False)
def fetch(self, request, *args, **kwargs):
try:
fid = request.data["fid"]
......@@ -175,7 +175,7 @@ class InboxItemViewSet(
qs = super().get_queryset()
return qs.filter(actor=self.request.user.actor)
@decorators.list_route(methods=["post"])
@decorators.action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = api_serializers.InboxItemActionSerializer(
......
......@@ -3,7 +3,7 @@ from django.core import paginator
from django.http import HttpResponse
from django.urls import reverse
from rest_framework import exceptions, mixins, response, viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework.decorators import action
from funkwhale_api.common import preferences
from funkwhale_api.music import models as music_models
......@@ -23,7 +23,7 @@ class SharedViewSet(FederationMixin, viewsets.GenericViewSet):
authentication_classes = [authentication.SignatureAuthentication]
renderer_classes = [renderers.ActivityPubRenderer]
@list_route(methods=["post"])
@action(methods=["post"], detail=False)
def inbox(self, request, *args, **kwargs):
if request.method.lower() == "post" and request.actor is None:
raise exceptions.AuthenticationFailed(
......@@ -42,7 +42,7 @@ class ActorViewSet(FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericV
queryset = models.Actor.objects.local().select_related("user")
serializer_class = serializers.ActorSerializer
@detail_route(methods=["get", "post"])
@action(methods=["get", "post"], detail=True)
def inbox(self, request, *args, **kwargs):
if request.method.lower() == "post" and request.actor is None:
raise exceptions.AuthenticationFailed(
......@@ -52,17 +52,17 @@ class ActorViewSet(FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericV
activity.receive(activity=request.data, on_behalf_of=request.actor)
return response.Response({}, status=200)
@detail_route(methods=["get", "post"])
@action(methods=["get", "post"], detail=True)
def outbox(self, request, *args, **kwargs):
return response.Response({}, status=200)
@detail_route(methods=["get"])
@action(methods=["get"], detail=True)
def followers(self, request, *args, **kwargs):
self.get_object()
# XXX to implement
return response.Response({})
@detail_route(methods=["get"])
@action(methods=["get"], detail=True)
def following(self, request, *args, **kwargs):
self.get_object()
# XXX to implement
......@@ -74,7 +74,7 @@ class WellKnownViewSet(viewsets.GenericViewSet):
permission_classes = []
renderer_classes = [renderers.JSONRenderer, renderers.WebfingerRenderer]
@list_route(methods=["get"])
@action(methods=["get"], detail=False)
def nodeinfo(self, request, *args, **kwargs):
if not preferences.get("instance__nodeinfo_enabled"):
return HttpResponse(status=404)
......@@ -88,7 +88,7 @@ class WellKnownViewSet(viewsets.GenericViewSet):
}
return response.Response(data)
@list_route(methods=["get"])
@action(methods=["get"], detail=False)
def webfinger(self, request, *args, **kwargs):
if not preferences.get("federation__enabled"):
return HttpResponse(status=405)
......@@ -180,7 +180,7 @@ class MusicLibraryViewSet(
return response.Response(data)
@detail_route(methods=["get"])
@action(methods=["get"], detail=True)
def followers(self, request, *args, **kwargs):
self.get_object()
# XXX Implement this
......
from rest_framework import mixins, response, viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework import decorators as rest_decorators
from django.shortcuts import get_object_or_404
from funkwhale_api.common import preferences, decorators
......@@ -35,7 +35,7 @@ class ManageUploadViewSet(
"duration",
]
@list_route(methods=["post"])
@rest_decorators.action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = serializers.ManageUploadActionSerializer(
......@@ -87,7 +87,7 @@ class ManageInvitationViewSet(
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
@list_route(methods=["post"])
@rest_decorators.action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = serializers.ManageInvitationActionSerializer(
......@@ -125,14 +125,14 @@ class ManageDomainViewSet(
"instance_policy",
]
@detail_route(methods=["get"])
@rest_decorators.action(methods=["get"], detail=True)
def nodeinfo(self, request, *args, **kwargs):
domain = self.get_object()
federation_tasks.update_domain_nodeinfo(domain_name=domain.name)
domain.refresh_from_db()
return response.Response(domain.nodeinfo, status=200)
@detail_route(methods=["get"])
@rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs):
domain = self.get_object()
return response.Response(domain.get_stats(), status=200)
......@@ -176,7 +176,7 @@ class ManageActorViewSet(
return obj
@detail_route(methods=["get"])
@rest_decorators.action(methods=["get"], detail=True)
def stats(self, request, *args, **kwargs):
domain = self.get_object()
return response.Response(domain.get_stats(), status=200)
......
......@@ -11,7 +11,7 @@ from rest_framework import mixins
from rest_framework import permissions
from rest_framework import settings as rest_settings
from rest_framework import views, viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework.decorators import action
from rest_framework.response import Response
from taggit.models import Tag
......@@ -28,25 +28,25 @@ logger = logging.getLogger(__name__)
def get_libraries(filter_uploads):
def view(self, request, *args, **kwargs):
def libraries(self, request, *args, **kwargs):
obj = self.get_object()
actor = utils.get_actor_from_request(request)
uploads = models.Upload.objects.all()
uploads = filter_uploads(obj, uploads)
uploads = uploads.playable_by(actor)
libraries = models.Library.objects.filter(
qs = models.Library.objects.filter(
pk__in=uploads.values_list("library", flat=True)
).annotate(_uploads_count=Count("uploads"))
libraries = libraries.select_related("actor")
page = self.paginate_queryset(libraries)
qs = qs.select_related("actor")
page = self.paginate_queryset(qs)
if page is not None:
serializer = federation_api_serializers.LibrarySerializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = federation_api_serializers.LibrarySerializer(libraries, many=True)
serializer = federation_api_serializers.LibrarySerializer(qs, many=True)
return Response(serializer.data)
return view
return libraries
class TagViewSetMixin(object):
......@@ -73,7 +73,7 @@ class ArtistViewSet(viewsets.ReadOnlyModelViewSet):
)
return queryset.prefetch_related(Prefetch("albums", queryset=albums))
libraries = detail_route(methods=["get"])(
libraries = action(methods=["get"], detail=True)(
get_libraries(
filter_uploads=lambda o, uploads: uploads.filter(
Q(track__artist=o) | Q(track__album__artist=o)
......@@ -101,7 +101,7 @@ class AlbumViewSet(viewsets.ReadOnlyModelViewSet):
qs = queryset.prefetch_related(Prefetch("tracks", queryset=tracks))
return qs
libraries = detail_route(methods=["get"])(
libraries = action(methods=["get"], detail=True)(
get_libraries(filter_uploads=lambda o, uploads: uploads.filter(track__album=o))
)
......@@ -144,7 +144,9 @@ class LibraryViewSet(
)
instance.delete()
@detail_route(methods=["get"])
follows = action
@action(methods=["get"], detail=True)
@transaction.non_atomic_requests
def follows(self, request, *args, **kwargs):
library = self.get_object()
......@@ -193,7 +195,7 @@ class TrackViewSet(TagViewSetMixin, viewsets.ReadOnlyModelViewSet):
)
return queryset
@detail_route(methods=["get"])
@action(methods=["get"], detail=True)
@transaction.non_atomic_requests
def lyrics(self, request, *args, **kwargs):
try:
......@@ -218,7 +220,7 @@ class TrackViewSet(TagViewSetMixin, viewsets.ReadOnlyModelViewSet):
serializer = serializers.LyricsSerializer(lyrics)
return Response(serializer.data)
libraries = detail_route(methods=["get"])(
libraries = action(methods=["get"], detail=True)(
get_libraries(filter_uploads=lambda o, uploads: uploads.filter(track=o))
)
......@@ -388,7 +390,7 @@ class UploadViewSet(
qs = super().get_queryset()
return qs.filter(library__actor=self.request.user.actor)
@list_route(methods=["post"])
@action(methods=["post"], detail=False)
def action(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = serializers.UploadActionSerializer(request.data, queryset=queryset)
......
from rest_framework import viewsets
from rest_framework.decorators import list_route
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.views import APIView
......@@ -47,19 +47,19 @@ class ReleaseBrowse(APIView):
class SearchViewSet(viewsets.ViewSet):
permission_classes = [ConditionalAuthentication]
@list_route(methods=["get"])
@action(methods=["get"], detail=False)
def recordings(self, request, *args, **kwargs):
query = request.GET["query"]
results = api.recordings.search(query)
return Response(results)
@list_route(methods=["get"])
@action(methods=["get"], detail=False)
def releases(self, request, *args, **kwargs):
query = request.GET["query"]
results = api.releases.search(query)
return Response(results)
@list_route(methods=["get"])
@action(methods=["get"], detail=False)
def artists(self, request, *args, **kwargs):
query = request.GET["query"]
results = api.artists.search(query)
......
from django.db import transaction
from django.db.models import Count
from rest_framework import exceptions, mixins, viewsets
from rest_framework.decorators import detail_route
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.response import Response
......@@ -36,7 +36,7 @@ class PlaylistViewSet(
filterset_class = filters.PlaylistFilter
ordering_fields = ("id", "name", "creation_date", "modification_date")
@detail_route(methods=["get"])
@action(methods=["get"], detail=True)
def tracks(self, request, *args, **kwargs):
playlist = self.get_object()
plts = playlist.playlist_tracks.all().for_nested_serialization(
......@@ -46,7 +46,7 @@ class PlaylistViewSet(
data = {"count": len(plts), "results": serializer.data}
return Response(data, status=200)
@detail_route(methods=["post"])
@action(methods=["post"], detail=True)
@transaction.atomic
def add(self, request, *args, **kwargs):
playlist = self.get_object()
......@@ -67,7 +67,7 @@ class PlaylistViewSet(
data = {"count": len(plts), "results": serializer.data}
return Response(data, status=201)
@detail_route(methods=["delete"])
@action(methods=["delete"], detail=True)
@transaction.atomic
def clear(self, request, *args, **kwargs):
playlist = self.get_object()
......
from django.db.models import Q
from rest_framework import mixins, permissions, status, viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework.decorators import action
from rest_framework.response import Response
from funkwhale_api.common import permissions as common_permissions
......@@ -40,7 +40,7 @@ class RadioViewSet(
def perform_update(self, serializer):
return serializer.save(user=self.request.user)
@detail_route(methods=["get"])
@action(methods=["get"], detail=True)
def tracks(self, request, *args, **kwargs):
radio = self.get_object()
tracks = radio.get_candidates().for_nested_serialization()
......@@ -50,14 +50,14 @@ class RadioViewSet(
serializer = TrackSerializer(page, many=True)
return self.get_paginated_response(serializer.data)
@list_route(methods=["get"])
@action(methods=["get"], detail=False)
def filters(self, request, *args, **kwargs):
serializer = serializers.FilterSerializer(
filters.registry.exposed_filters, many=True
)
return Response(serializer.data)
@list_route(methods=["post"])
@action(methods=["post"], detail=False)
def validate(self, request, *args, **kwargs):
try:
f_list = request.data["filters"]
......
import datetime
import functools
from django.conf import settings
from django.utils import timezone
from rest_framework import exceptions
from rest_framework import permissions as rest_permissions
from rest_framework import renderers, response, viewsets
from rest_framework.decorators import list_route
from rest_framework.decorators import action
from rest_framework.serializers import ValidationError
import funkwhale_api
......@@ -25,6 +26,7 @@ def find_object(
queryset, model_field="pk", field="id", cast=int, filter_playable=False
):
def decorator(func):
@functools.wraps(func)
def inner(self, request, *args, **kwargs):
data = request.GET or request.POST
try:
......@@ -110,12 +112,13 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200)
@list_route(methods=["get", "post"], permission_classes=[])
@action(detail=False, methods=["get", "post"], permission_classes=[])
def ping(self, request, *args, **kwargs):
data = {"status": "ok", "version": "1.16.0"}
return response.Response(data, status=200)
@list_route(
@action(
detail=False,
methods=["get", "post"],
url_name="get_license",
permissions_classes=[],
......@@ -136,7 +139,12 @@ class SubsonicViewSet(viewsets.GenericViewSet):
}
return response.Response(data, status=200)
@list_route(methods=["get", "post"], url_name="get_artists", url_path="getArtists")
@action(
detail=False,
methods=["get", "post"],
url_name="get_artists",
url_path="getArtists",
)
def get_artists(self, request, *args, **kwargs):
artists = music_models.Artist.objects.all().playable_by(
utils.get_actor_from_request(request)
......@@ -146,7 +154,12 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="get_indexes", url_path="getIndexes")
@action(
detail=False,
methods=["get", "post"],
url_name="get_indexes",
url_path="getIndexes",
)
def get_indexes(self, request, *args, **kwargs):
artists = music_models.Artist.objects.all().playable_by(
utils.get_actor_from_request(request)
......@@ -156,7 +169,12 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="get_artist", url_path="getArtist")
@action(
detail=False,
methods=["get", "post"],
url_name="get_artist",
url_path="getArtist",
)
@find_object(music_models.Artist.objects.all(), filter_playable=True)
def get_artist(self, request, *args, **kwargs):
artist = kwargs.pop("obj")
......@@ -165,7 +183,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="get_song", url_path="getSong")
@action(
detail=False, methods=["get", "post"], url_name="get_song", url_path="getSong"
)
@find_object(music_models.Track.objects.all(), filter_playable=True)
def get_song(self, request, *args, **kwargs):
track = kwargs.pop("obj")
......@@ -174,8 +194,11 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200)
@list_route(
methods=["get", "post"], url_name="get_artist_info2", url_path="getArtistInfo2"
@action(
detail=False,
methods=["get", "post"],
url_name="get_artist_info2",
url_path="getArtistInfo2",
)
@find_object(music_models.Artist.objects.all(), filter_playable=True)
def get_artist_info2(self, request, *args, **kwargs):
......@@ -183,7 +206,9 @@ class SubsonicViewSet(viewsets.GenericViewSet):
return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="get_album", url_path="getAlbum")
@action(
detail=False, methods=["get", "post"], url_name="get_album", url_path="getAlbum"
)
@find_object(
music_models.Album.objects.select_related("artist"), filter_playable=True
)
......@@ -193,7 +218,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
payload = {"album": data}
return response.Response(payload, status=200)
@list_route(methods=["get", "post"], url_name="stream", url_path="stream")
@action(detail=False, methods=["get", "post"], url_name="stream", url_path="stream")
@find_object(music_models.Track.objects.all(), filter_playable=True)
def stream(self, request, *args, **kwargs):
data = request.GET or request.POST
......@@ -208,30 +233,36 @@ class SubsonicViewSet(viewsets.GenericViewSet):
format = None
return music_views.handle_serve(upload=upload, user=request.user, format=format)
@list_route(methods=["get", "post"], url_name="star", url_path="star")
@action(detail=False, methods=["get", "post"], url_name="star", url_path="star")
@find_object(music_models.Track.objects.all())
def star(self, request, *args, **kwargs):
track = kwargs.pop("obj")
TrackFavorite.add(user=request.user, track=track)
return response.Response({"status": "ok"})
@list_route(methods=["get", "post"], url_name="unstar", url_path="unstar")
@action(detail=False, methods=["get", "post"], url_name="unstar", url_path="unstar")
@find_object(music_models.Track.objects.all())
def unstar(self, request, *args, **kwargs):
track = kwargs.pop("obj")
request.user.track_favorites.filter(track=track).delete()
return response.Response({"status": "ok"})
@list_route(
methods=["get", "post"], url_name="get_starred2", url_path="getStarred2"
@action(
detail=False,
methods=["get", "post"],
url_name="get_starred2",
url_path="getStarred2",
)
def get_starred2(self, request, *args, **kwargs):
favorites = request.user.track_favorites.all()
data = {"starred2": {"song": serializers.get_starred_tracks_data(favorites)}}
return response.Response(data)
@list_route(
methods=["get", "post"], url_name="get_random_songs", url_path="getRandomSongs"
@action(
detail=False,
methods=["get", "post"],
url_name="get_random_songs",
url_path="getRandomSongs",
)
def get_random_songs(self, request, *args, **kwargs):
data = request.GET or request.POST
......@@ -253,14 +284,22 @@ class SubsonicViewSet(viewsets.GenericViewSet):
}
return response.Response(data)
@list_route(methods=["get", "post"], url_name="get_starred", url_path="getStarred")
@action(
detail=False,
methods=["get", "post"],
url_name="get_starred",
url_path="getStarred",
)