See #432: added admin API endpoints to retrieve and delete tags
This commit is contained in:
parent
c885c10be1
commit
13f36beec3
|
@ -13,6 +13,7 @@ from funkwhale_api.federation import utils as federation_utils
|
||||||
from funkwhale_api.moderation import models as moderation_models
|
from funkwhale_api.moderation import models as moderation_models
|
||||||
from funkwhale_api.music import models as music_models
|
from funkwhale_api.music import models as music_models
|
||||||
from funkwhale_api.users import models as users_models
|
from funkwhale_api.users import models as users_models
|
||||||
|
from funkwhale_api.tags import models as tags_models
|
||||||
|
|
||||||
|
|
||||||
class ActorField(forms.CharField):
|
class ActorField(forms.CharField):
|
||||||
|
@ -340,3 +341,11 @@ class ManageInstancePolicyFilterSet(filters.FilterSet):
|
||||||
"silence_notifications",
|
"silence_notifications",
|
||||||
"reject_media",
|
"reject_media",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTagFilterSet(filters.FilterSet):
|
||||||
|
q = fields.SearchFilter(search_fields=["name"])
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = tags_models.Tag
|
||||||
|
fields = ["q"]
|
||||||
|
|
|
@ -10,6 +10,7 @@ from funkwhale_api.federation import tasks as federation_tasks
|
||||||
from funkwhale_api.moderation import models as moderation_models
|
from funkwhale_api.moderation import models as moderation_models
|
||||||
from funkwhale_api.music import models as music_models
|
from funkwhale_api.music import models as music_models
|
||||||
from funkwhale_api.music import serializers as music_serializers
|
from funkwhale_api.music import serializers as music_serializers
|
||||||
|
from funkwhale_api.tags import models as tags_models
|
||||||
from funkwhale_api.users import models as users_models
|
from funkwhale_api.users import models as users_models
|
||||||
|
|
||||||
from . import filters
|
from . import filters
|
||||||
|
@ -564,3 +565,30 @@ class ManageUploadSerializer(serializers.ModelSerializer):
|
||||||
"track",
|
"track",
|
||||||
"library",
|
"library",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTagSerializer(ManageBaseAlbumSerializer):
|
||||||
|
|
||||||
|
tracks_count = serializers.SerializerMethodField()
|
||||||
|
albums_count = serializers.SerializerMethodField()
|
||||||
|
artists_count = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = tags_models.Tag
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"creation_date",
|
||||||
|
"tracks_count",
|
||||||
|
"albums_count",
|
||||||
|
"artists_count",
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_tracks_count(self, obj):
|
||||||
|
return getattr(obj, "_tracks_count", None)
|
||||||
|
|
||||||
|
def get_albums_count(self, obj):
|
||||||
|
return getattr(obj, "_albums_count", None)
|
||||||
|
|
||||||
|
def get_artists_count(self, obj):
|
||||||
|
return getattr(obj, "_artists_count", None)
|
||||||
|
|
|
@ -24,6 +24,7 @@ users_router.register(r"invitations", views.ManageInvitationViewSet, "invitation
|
||||||
|
|
||||||
other_router = routers.OptionalSlashRouter()
|
other_router = routers.OptionalSlashRouter()
|
||||||
other_router.register(r"accounts", views.ManageActorViewSet, "accounts")
|
other_router.register(r"accounts", views.ManageActorViewSet, "accounts")
|
||||||
|
other_router.register(r"tags", views.ManageTagViewSet, "tags")
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(
|
url(
|
||||||
|
|
|
@ -2,7 +2,7 @@ from rest_framework import mixins, response, viewsets
|
||||||
from rest_framework import decorators as rest_decorators
|
from rest_framework import decorators as rest_decorators
|
||||||
|
|
||||||
from django.db.models import Count, Prefetch, Q, Sum, OuterRef, Subquery
|
from django.db.models import Count, Prefetch, Q, Sum, OuterRef, Subquery
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce, Length
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from funkwhale_api.common import models as common_models
|
from funkwhale_api.common import models as common_models
|
||||||
|
@ -14,6 +14,7 @@ from funkwhale_api.history import models as history_models
|
||||||
from funkwhale_api.music import models as music_models
|
from funkwhale_api.music import models as music_models
|
||||||
from funkwhale_api.moderation import models as moderation_models
|
from funkwhale_api.moderation import models as moderation_models
|
||||||
from funkwhale_api.playlists import models as playlists_models
|
from funkwhale_api.playlists import models as playlists_models
|
||||||
|
from funkwhale_api.tags import models as tags_models
|
||||||
from funkwhale_api.users import models as users_models
|
from funkwhale_api.users import models as users_models
|
||||||
|
|
||||||
|
|
||||||
|
@ -452,3 +453,43 @@ class ManageInstancePolicyViewSet(
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
serializer.save(actor=self.request.user.actor)
|
serializer.save(actor=self.request.user.actor)
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTagViewSet(
|
||||||
|
mixins.ListModelMixin,
|
||||||
|
mixins.RetrieveModelMixin,
|
||||||
|
mixins.DestroyModelMixin,
|
||||||
|
mixins.CreateModelMixin,
|
||||||
|
viewsets.GenericViewSet,
|
||||||
|
):
|
||||||
|
lookup_field = "name"
|
||||||
|
queryset = (
|
||||||
|
tags_models.Tag.objects.all()
|
||||||
|
.order_by("-creation_date")
|
||||||
|
.annotate(items_count=Count("tagged_items"))
|
||||||
|
.annotate(length=Length("name"))
|
||||||
|
)
|
||||||
|
serializer_class = serializers.ManageTagSerializer
|
||||||
|
filterset_class = filters.ManageTagFilterSet
|
||||||
|
required_scope = "instance:libraries"
|
||||||
|
ordering_fields = ["id", "creation_date", "name", "items_count", "length"]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
album_ct = ContentType.objects.get_for_model(music_models.Album)
|
||||||
|
track_ct = ContentType.objects.get_for_model(music_models.Track)
|
||||||
|
artist_ct = ContentType.objects.get_for_model(music_models.Artist)
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
_albums_count=Count(
|
||||||
|
"tagged_items", filter=Q(tagged_items__content_type=album_ct)
|
||||||
|
),
|
||||||
|
_tracks_count=Count(
|
||||||
|
"tagged_items", filter=Q(tagged_items__content_type=track_ct)
|
||||||
|
),
|
||||||
|
_artists_count=Count(
|
||||||
|
"tagged_items", filter=Q(tagged_items__content_type=artist_ct)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return queryset
|
||||||
|
|
|
@ -496,3 +496,22 @@ def test_action_serializer_delete(factory, serializer_class, factories):
|
||||||
s.handle_delete(objects[0].__class__.objects.all())
|
s.handle_delete(objects[0].__class__.objects.all())
|
||||||
|
|
||||||
assert objects[0].__class__.objects.count() == 0
|
assert objects[0].__class__.objects.count() == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_manage_tag_serializer(factories):
|
||||||
|
tag = factories["tags.Tag"]()
|
||||||
|
|
||||||
|
setattr(tag, "_tracks_count", 42)
|
||||||
|
setattr(tag, "_albums_count", 54)
|
||||||
|
setattr(tag, "_artists_count", 66)
|
||||||
|
expected = {
|
||||||
|
"id": tag.id,
|
||||||
|
"name": tag.name,
|
||||||
|
"creation_date": tag.creation_date.isoformat().split("+")[0] + "Z",
|
||||||
|
"tracks_count": 42,
|
||||||
|
"albums_count": 54,
|
||||||
|
"artists_count": 66,
|
||||||
|
}
|
||||||
|
s = serializers.ManageTagSerializer(tag)
|
||||||
|
|
||||||
|
assert s.data == expected
|
||||||
|
|
|
@ -377,3 +377,31 @@ def test_upload_delete(factories, superuser_api_client):
|
||||||
response = superuser_api_client.delete(url)
|
response = superuser_api_client.delete(url)
|
||||||
|
|
||||||
assert response.status_code == 204
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
|
||||||
|
def test_tag_detail(factories, superuser_api_client):
|
||||||
|
tag = factories["tags.Tag"]()
|
||||||
|
url = reverse("api:v1:manage:tags-detail", kwargs={"name": tag.name})
|
||||||
|
response = superuser_api_client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data["name"] == tag.name
|
||||||
|
|
||||||
|
|
||||||
|
def test_tag_list(factories, superuser_api_client, settings):
|
||||||
|
tag = factories["tags.Tag"]()
|
||||||
|
url = reverse("api:v1:manage:tags-list")
|
||||||
|
response = superuser_api_client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
assert response.data["count"] == 1
|
||||||
|
assert response.data["results"][0]["name"] == tag.name
|
||||||
|
|
||||||
|
|
||||||
|
def test_tag_delete(factories, superuser_api_client):
|
||||||
|
tag = factories["tags.Tag"]()
|
||||||
|
url = reverse("api:v1:manage:tags-detail", kwargs={"name": tag.name})
|
||||||
|
response = superuser_api_client.delete(url)
|
||||||
|
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
Loading…
Reference in New Issue