Added a new ?related=obj_id filter for artists, albums and tracks, based on tags

environments/review-docs-api-d-byp130/deployments/5273
Agate 2020-06-22 14:39:50 +02:00
rodzic ae7819048e
commit d50cce36e2
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6B501DFD73514E14
5 zmienionych plików z 87 dodań i 0 usunięć

Wyświetl plik

@ -9,6 +9,7 @@ from funkwhale_api.common import fields
from funkwhale_api.common import filters as common_filters
from funkwhale_api.common import search
from funkwhale_api.moderation import filters as moderation_filters
from funkwhale_api.tags import filters as tags_filters
from . import models
from . import utils
@ -24,6 +25,28 @@ def filter_tags(queryset, name, value):
TAG_FILTER = common_filters.MultipleQueryFilter(method=filter_tags)
class RelatedFilterSet(filters.FilterSet):
related_type = int
related_field = "pk"
related = filters.CharFilter(field_name="_", method="filter_related")
def filter_related(self, queryset, name, value):
if not value:
return queryset.none()
try:
pk = self.related_type(value)
except (TypeError, ValueError):
return queryset.none()
try:
obj = queryset.model.objects.get(**{self.related_field: pk})
except queryset.model.DoesNotExist:
return queryset.none()
queryset = queryset.exclude(pk=obj.pk)
return tags_filters.get_by_similar_tags(queryset, obj.get_tags())
class ChannelFilterSet(filters.FilterSet):
channel = filters.CharFilter(field_name="_", method="filter_channel")
@ -70,6 +93,7 @@ class LibraryFilterSet(filters.FilterSet):
class ArtistFilter(
RelatedFilterSet,
LibraryFilterSet,
audio_filters.IncludeChannelsFilterSet,
moderation_filters.HiddenContentFilterSet,
@ -88,6 +112,7 @@ class ArtistFilter(
("creation_date", "creation_date"),
("modification_date", "modification_date"),
("?", "random"),
("tag_matches", "related"),
)
)
@ -109,6 +134,7 @@ class ArtistFilter(
class TrackFilter(
RelatedFilterSet,
ChannelFilterSet,
LibraryFilterSet,
audio_filters.IncludeChannelsFilterSet,
@ -140,6 +166,7 @@ class TrackFilter(
("artist__name", "artist__name"),
("artist__modification_date", "artist__modification_date"),
("?", "random"),
("tag_matches", "related"),
)
)
@ -217,6 +244,7 @@ class UploadFilter(audio_filters.IncludeChannelsFilterSet):
class AlbumFilter(
RelatedFilterSet,
ChannelFilterSet,
LibraryFilterSet,
audio_filters.IncludeChannelsFilterSet,
@ -239,6 +267,7 @@ class AlbumFilter(
("title", "title"),
("artist__modification_date", "artist__modification_date"),
("?", "random"),
("tag_matches", "related"),
)
)

Wyświetl plik

@ -1,3 +1,5 @@
from django.db import models as dj_models
import django_filters
from django_filters import rest_framework as filters
@ -19,3 +21,19 @@ class TagFilter(filters.FilterSet):
class Meta:
model = models.Tag
fields = {"q": ["exact"], "name": ["exact", "startswith"]}
def get_by_similar_tags(qs, tags):
"""
Return a queryset of obects with at least one matching tag.
Annotate the queryset so you can order later by number of matches.
"""
qs = qs.filter(tagged_items__tag__name__in=tags).annotate(
tag_matches=dj_models.Count(
dj_models.Case(
dj_models.When(tagged_items__tag__name__in=tags, then=1),
output_field=dj_models.IntegerField(),
)
)
)
return qs.distinct()

Wyświetl plik

@ -203,3 +203,41 @@ def test_track_filter_artist_includes_album_artist(
)
assert filterset.qs == [track2, track1]
@pytest.mark.parametrize(
"factory_name, filterset_class",
[
("music.Track", filters.TrackFilter),
("music.Artist", filters.ArtistFilter),
("music.Album", filters.AlbumFilter),
],
)
def test_filter_tag_related(
factory_name,
filterset_class,
factories,
anonymous_user,
queryset_equal_list,
mocker,
):
factories["tags.Tag"](name="foo")
factories["tags.Tag"](name="bar")
factories["tags.Tag"](name="baz")
factories["tags.Tag"]()
factories["tags.Tag"]()
matches = [
factories[factory_name](set_tags=["foo", "bar", "baz", "noop"]),
factories[factory_name](set_tags=["foo", "baz", "noop"]),
factories[factory_name](set_tags=["baz", "noop"]),
]
factories[factory_name](set_tags=["noop"]),
obj = factories[factory_name](set_tags=["foo", "bar", "baz"])
filterset = filterset_class(
{"related": obj.pk, "ordering": "-related"},
request=mocker.Mock(user=anonymous_user, actor=None),
queryset=obj.__class__.objects.all(),
)
assert filterset.qs == matches

Wyświetl plik

@ -0,0 +1 @@
Support ordering=random for artists, albums, tracks and channels endpoints (#1145)

Wyświetl plik

@ -0,0 +1 @@
Added a new ?related=obj_id filter for artists, albums and tracks, based on tags