mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2024-11-25 18:43:47 +03:00
updated api, added music endpoints
This commit is contained in:
parent
aff1cc0591
commit
32912620a4
|
@ -1,5 +1,7 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from akarpov.utils.models import get_model_user_field
|
||||||
|
|
||||||
|
|
||||||
class RecursiveField(serializers.Serializer):
|
class RecursiveField(serializers.Serializer):
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
|
@ -9,6 +11,19 @@ def to_representation(self, value):
|
||||||
|
|
||||||
class SetUserModelSerializer(serializers.ModelSerializer):
|
class SetUserModelSerializer(serializers.ModelSerializer):
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
if self.context["request"].user.is_authenticated:
|
||||||
creator = self.context["request"].user
|
creator = self.context["request"].user
|
||||||
obj = self.Meta.model.objects.create(creator=creator, **validated_data)
|
else:
|
||||||
|
creator = None
|
||||||
|
validated_data[
|
||||||
|
get_model_user_field(
|
||||||
|
self.Meta.model._meta.app_label, self.Meta.model._meta.model_name
|
||||||
|
)
|
||||||
|
] = creator
|
||||||
|
try:
|
||||||
|
obj = self.Meta.model.objects.create(**validated_data)
|
||||||
|
except TypeError:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
{"detail": "You need to login to create this object"}
|
||||||
|
)
|
||||||
return obj
|
return obj
|
||||||
|
|
9
akarpov/music/api/filters.py
Normal file
9
akarpov/music/api/filters.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from django_filters import rest_framework as filters
|
||||||
|
|
||||||
|
from akarpov.music.models import Playlist
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistFilter(filters.FilterSet):
|
||||||
|
class Meta:
|
||||||
|
model = Playlist
|
||||||
|
fields = ()
|
|
@ -2,37 +2,40 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from akarpov.common.api.serializers import SetUserModelSerializer
|
from akarpov.common.api.serializers import SetUserModelSerializer
|
||||||
from akarpov.music.models import Album, Author, Playlist, Song
|
from akarpov.music.models import (
|
||||||
|
Album,
|
||||||
|
Author,
|
||||||
|
Playlist,
|
||||||
|
PlaylistSong,
|
||||||
|
Song,
|
||||||
|
SongUserRating,
|
||||||
|
)
|
||||||
from akarpov.users.api.serializers import UserPublicInfoSerializer
|
from akarpov.users.api.serializers import UserPublicInfoSerializer
|
||||||
|
|
||||||
|
|
||||||
class AuthorSerializer(serializers.ModelSerializer):
|
class AuthorSerializer(serializers.ModelSerializer):
|
||||||
url = serializers.SerializerMethodField(method_name="get_url")
|
|
||||||
|
|
||||||
@extend_schema_field(serializers.URLField)
|
|
||||||
def get_url(self, obj):
|
|
||||||
return obj.get_absolute_url()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Author
|
model = Author
|
||||||
fields = ["name", "link", "image_cropped", "url"]
|
fields = ["name", "slug", "link", "image_cropped"]
|
||||||
|
|
||||||
|
|
||||||
class AlbumSerializer(serializers.ModelSerializer):
|
class AlbumSerializer(serializers.ModelSerializer):
|
||||||
url = serializers.SerializerMethodField(method_name="get_url")
|
|
||||||
|
|
||||||
@extend_schema_field(serializers.URLField)
|
|
||||||
def get_url(self, obj):
|
|
||||||
return obj.get_absolute_url()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Album
|
model = Album
|
||||||
fields = ["name", "link", "image_cropped", "url"]
|
fields = ["name", "slug", "link", "image_cropped"]
|
||||||
|
|
||||||
|
|
||||||
class SongSerializer(serializers.ModelSerializer):
|
class SongSerializer(serializers.ModelSerializer):
|
||||||
authors = AuthorSerializer(many=True)
|
authors = AuthorSerializer(many=True)
|
||||||
album = AlbumSerializer()
|
album = AlbumSerializer()
|
||||||
|
liked = serializers.SerializerMethodField(method_name="get_liked")
|
||||||
|
|
||||||
|
@extend_schema_field(serializers.BooleanField)
|
||||||
|
def get_liked(self, obj):
|
||||||
|
if self.context["request"].user.is_authenticated:
|
||||||
|
return SongUserRating.objects.filter(
|
||||||
|
song=obj, user=self.context["request"].user, like=True
|
||||||
|
).exists()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Song
|
model = Song
|
||||||
|
@ -45,6 +48,7 @@ class Meta:
|
||||||
"file",
|
"file",
|
||||||
"authors",
|
"authors",
|
||||||
"album",
|
"album",
|
||||||
|
"liked",
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"slug": {"read_only": True},
|
"slug": {"read_only": True},
|
||||||
|
@ -58,12 +62,15 @@ class ListSongSerializer(SetUserModelSerializer):
|
||||||
album = serializers.CharField(source="album.name", read_only=True)
|
album = serializers.CharField(source="album.name", read_only=True)
|
||||||
liked = serializers.SerializerMethodField(method_name="get_liked")
|
liked = serializers.SerializerMethodField(method_name="get_liked")
|
||||||
|
|
||||||
|
@extend_schema_field(serializers.BooleanField)
|
||||||
def get_liked(self, obj):
|
def get_liked(self, obj):
|
||||||
|
if "likes" in self.context:
|
||||||
|
return self.context["likes"]
|
||||||
return obj.id in self.context["likes_ids"]
|
return obj.id in self.context["likes_ids"]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Song
|
model = Song
|
||||||
fields = ["name", "slug", "file", "image_cropped", "length", "album"]
|
fields = ["name", "slug", "file", "image_cropped", "length", "album", "liked"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"slug": {"read_only": True},
|
"slug": {"read_only": True},
|
||||||
"image_cropped": {"read_only": True},
|
"image_cropped": {"read_only": True},
|
||||||
|
@ -73,14 +80,15 @@ class Meta:
|
||||||
|
|
||||||
|
|
||||||
class PlaylistSerializer(SetUserModelSerializer):
|
class PlaylistSerializer(SetUserModelSerializer):
|
||||||
creator = UserPublicInfoSerializer()
|
creator = UserPublicInfoSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Playlist
|
model = Playlist
|
||||||
fields = ["name", "slug", "private", "creator"]
|
fields = ["name", "length", "slug", "private", "creator"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"slug": {"read_only": True},
|
"slug": {"read_only": True},
|
||||||
"creator": {"read_only": True},
|
"creator": {"read_only": True},
|
||||||
|
"length": {"read_only": True},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,3 +103,131 @@ class Meta:
|
||||||
"slug": {"read_only": True},
|
"slug": {"read_only": True},
|
||||||
"creator": {"read_only": True},
|
"creator": {"read_only": True},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AddSongToPlaylistSerializer(serializers.ModelSerializer):
|
||||||
|
song = serializers.SlugField()
|
||||||
|
playlist = serializers.SlugField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Playlist
|
||||||
|
fields = ["song", "playlist"]
|
||||||
|
extra_kwargs = {
|
||||||
|
"song": {"write_only": True},
|
||||||
|
"playlist": {"write_only": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
if not Playlist.objects.filter(slug=attrs["playlist"]).exists():
|
||||||
|
raise serializers.ValidationError("Playlist not found")
|
||||||
|
if not Song.objects.filter(slug=attrs["song"]).exists():
|
||||||
|
raise serializers.ValidationError("Song not found")
|
||||||
|
if self.context["create"]:
|
||||||
|
if PlaylistSong.objects.filter(
|
||||||
|
playlist__slug=attrs["playlist"], song__slug=attrs["song"]
|
||||||
|
).exists():
|
||||||
|
raise serializers.ValidationError("Song already in playlist")
|
||||||
|
else:
|
||||||
|
if not PlaylistSong.objects.filter(
|
||||||
|
playlist__slug=attrs["playlist"], song__slug=attrs["song"]
|
||||||
|
).exists():
|
||||||
|
raise serializers.ValidationError("Song not in playlist")
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
playlist = Playlist.objects.get(slug=validated_data["playlist"])
|
||||||
|
song = Song.objects.get(slug=validated_data["song"])
|
||||||
|
if not self.context["create"]:
|
||||||
|
playlist_song = PlaylistSong.objects.get(
|
||||||
|
playlist=playlist, song=song
|
||||||
|
).delete()
|
||||||
|
else:
|
||||||
|
order = playlist.songs.count()
|
||||||
|
playlist_song = playlist.songs.create(song=song, order=order)
|
||||||
|
return playlist_song
|
||||||
|
|
||||||
|
|
||||||
|
class LikeDislikeSongSerializer(serializers.ModelSerializer):
|
||||||
|
song = serializers.SlugField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SongUserRating
|
||||||
|
fields = ["song"]
|
||||||
|
extra_kwargs = {
|
||||||
|
"song": {"write_only": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
if not Song.objects.filter(slug=attrs["song"]).exists():
|
||||||
|
raise serializers.ValidationError("Song not found")
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
song = Song.objects.get(slug=validated_data["song"])
|
||||||
|
if self.context["like"]:
|
||||||
|
if SongUserRating.objects.filter(
|
||||||
|
song=song, user=self.context["request"].user
|
||||||
|
).exists():
|
||||||
|
song_user_rating = SongUserRating.objects.get(
|
||||||
|
song=song, user=self.context["request"].user
|
||||||
|
)
|
||||||
|
if song_user_rating.like:
|
||||||
|
song_user_rating.delete()
|
||||||
|
else:
|
||||||
|
song_user_rating.like = True
|
||||||
|
song_user_rating.save()
|
||||||
|
else:
|
||||||
|
song_user_rating = SongUserRating.objects.create(
|
||||||
|
song=song, user=self.context["request"].user, like=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if SongUserRating.objects.filter(
|
||||||
|
song=song, user=self.context["request"].user
|
||||||
|
).exists():
|
||||||
|
song_user_rating = SongUserRating.objects.get(
|
||||||
|
song=song, user=self.context["request"].user
|
||||||
|
)
|
||||||
|
if not song_user_rating.like:
|
||||||
|
song_user_rating.delete()
|
||||||
|
else:
|
||||||
|
song_user_rating.like = False
|
||||||
|
song_user_rating.save()
|
||||||
|
else:
|
||||||
|
song_user_rating = SongUserRating.objects.create(
|
||||||
|
song=song, user=self.context["request"].user, like=False
|
||||||
|
)
|
||||||
|
return song_user_rating
|
||||||
|
|
||||||
|
|
||||||
|
class ListPlaylistSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Playlist
|
||||||
|
fields = ["name", "slug", "private"]
|
||||||
|
extra_kwargs = {
|
||||||
|
"slug": {"read_only": True},
|
||||||
|
"private": {"read_only": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FullAlbumSerializer(serializers.ModelSerializer):
|
||||||
|
songs = ListSongSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Album
|
||||||
|
fields = ["name", "link", "image", "songs"]
|
||||||
|
extra_kwargs = {
|
||||||
|
"link": {"read_only": True},
|
||||||
|
"image": {"read_only": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FullAuthorSerializer(serializers.ModelSerializer):
|
||||||
|
songs = ListSongSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Author
|
||||||
|
fields = ["name", "link", "image", "songs"]
|
||||||
|
extra_kwargs = {
|
||||||
|
"link": {"read_only": True},
|
||||||
|
"image": {"read_only": True},
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from akarpov.music.api.views import (
|
from akarpov.music.api.views import (
|
||||||
|
AddSongToPlaylistAPIView,
|
||||||
|
DislikeSongAPIView,
|
||||||
|
LikeSongAPIView,
|
||||||
|
ListAlbumsAPIView,
|
||||||
|
ListAuthorsAPIView,
|
||||||
ListCreatePlaylistAPIView,
|
ListCreatePlaylistAPIView,
|
||||||
ListCreateSongAPIView,
|
ListCreateSongAPIView,
|
||||||
|
ListDislikedSongsAPIView,
|
||||||
|
ListLikedSongsAPIView,
|
||||||
|
ListPublicPlaylistAPIView,
|
||||||
|
RemoveSongFromPlaylistAPIView,
|
||||||
|
RetrieveUpdateDestroyAlbumAPIView,
|
||||||
|
RetrieveUpdateDestroyAuthorAPIView,
|
||||||
RetrieveUpdateDestroyPlaylistAPIView,
|
RetrieveUpdateDestroyPlaylistAPIView,
|
||||||
RetrieveUpdateDestroySongAPIView,
|
RetrieveUpdateDestroySongAPIView,
|
||||||
)
|
)
|
||||||
|
@ -10,9 +21,16 @@
|
||||||
app_name = "music"
|
app_name = "music"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path("song/liked/", ListLikedSongsAPIView.as_view(), name="list_liked"),
|
||||||
|
path("song/disliked/", ListDislikedSongsAPIView.as_view(), name="list_disliked"),
|
||||||
path(
|
path(
|
||||||
"playlists/", ListCreatePlaylistAPIView.as_view(), name="list_create_playlist"
|
"playlists/", ListCreatePlaylistAPIView.as_view(), name="list_create_playlist"
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"playlists/public/",
|
||||||
|
ListPublicPlaylistAPIView.as_view(),
|
||||||
|
name="list_public",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"playlists/<str:slug>",
|
"playlists/<str:slug>",
|
||||||
RetrieveUpdateDestroyPlaylistAPIView.as_view(),
|
RetrieveUpdateDestroyPlaylistAPIView.as_view(),
|
||||||
|
@ -24,4 +42,20 @@
|
||||||
RetrieveUpdateDestroySongAPIView.as_view(),
|
RetrieveUpdateDestroySongAPIView.as_view(),
|
||||||
name="retrieve_update_delete_song",
|
name="retrieve_update_delete_song",
|
||||||
),
|
),
|
||||||
|
path("song/like/", LikeSongAPIView.as_view()),
|
||||||
|
path("song/dislike/", DislikeSongAPIView.as_view()),
|
||||||
|
path("playlists/add/", AddSongToPlaylistAPIView.as_view()),
|
||||||
|
path("playlists/remove/", RemoveSongFromPlaylistAPIView.as_view()),
|
||||||
|
path("albums/", ListAlbumsAPIView.as_view(), name="list_albums"),
|
||||||
|
path(
|
||||||
|
"albums/<str:slug>",
|
||||||
|
RetrieveUpdateDestroyAlbumAPIView.as_view(),
|
||||||
|
name="retrieve_update_delete_album",
|
||||||
|
),
|
||||||
|
path("authors/", ListAuthorsAPIView.as_view(), name="list_authors"),
|
||||||
|
path(
|
||||||
|
"authors/<str:slug>",
|
||||||
|
RetrieveUpdateDestroyAuthorAPIView.as_view(),
|
||||||
|
name="retrieve_update_delete_author",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,12 +3,19 @@
|
||||||
from akarpov.common.api.pagination import StandardResultsSetPagination
|
from akarpov.common.api.pagination import StandardResultsSetPagination
|
||||||
from akarpov.common.api.permissions import IsAdminOrReadOnly, IsCreatorOrReadOnly
|
from akarpov.common.api.permissions import IsAdminOrReadOnly, IsCreatorOrReadOnly
|
||||||
from akarpov.music.api.serializers import (
|
from akarpov.music.api.serializers import (
|
||||||
|
AddSongToPlaylistSerializer,
|
||||||
|
AlbumSerializer,
|
||||||
|
AuthorSerializer,
|
||||||
|
FullAlbumSerializer,
|
||||||
|
FullAuthorSerializer,
|
||||||
FullPlaylistSerializer,
|
FullPlaylistSerializer,
|
||||||
|
LikeDislikeSongSerializer,
|
||||||
|
ListPlaylistSerializer,
|
||||||
ListSongSerializer,
|
ListSongSerializer,
|
||||||
PlaylistSerializer,
|
PlaylistSerializer,
|
||||||
SongSerializer,
|
SongSerializer,
|
||||||
)
|
)
|
||||||
from akarpov.music.models import Playlist, Song, SongUserRating
|
from akarpov.music.models import Album, Author, Playlist, Song, SongUserRating
|
||||||
|
|
||||||
|
|
||||||
class LikedSongsContextMixin(generics.GenericAPIView):
|
class LikedSongsContextMixin(generics.GenericAPIView):
|
||||||
|
@ -26,11 +33,23 @@ def get_serializer_context(self):
|
||||||
|
|
||||||
|
|
||||||
class ListCreatePlaylistAPIView(generics.ListCreateAPIView):
|
class ListCreatePlaylistAPIView(generics.ListCreateAPIView):
|
||||||
permission_classes = [permissions.IsAuthenticated]
|
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
|
||||||
serializer_class = PlaylistSerializer
|
serializer_class = PlaylistSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Playlist.objects.filter(creator=self.request.user)
|
if self.request.user.is_authenticated:
|
||||||
|
return Playlist.objects.filter(creator=self.request.user).select_related(
|
||||||
|
"creator"
|
||||||
|
)
|
||||||
|
return Playlist.objects.filter(private=False).select_related("creator")
|
||||||
|
|
||||||
|
|
||||||
|
class ListPublicPlaylistAPIView(generics.ListAPIView):
|
||||||
|
permission_classes = [permissions.AllowAny]
|
||||||
|
serializer_class = PlaylistSerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Playlist.objects.filter(private=False)
|
||||||
|
|
||||||
|
|
||||||
class RetrieveUpdateDestroyPlaylistAPIView(
|
class RetrieveUpdateDestroyPlaylistAPIView(
|
||||||
|
@ -41,14 +60,11 @@ class RetrieveUpdateDestroyPlaylistAPIView(
|
||||||
permission_classes = [IsCreatorOrReadOnly]
|
permission_classes = [IsCreatorOrReadOnly]
|
||||||
serializer_class = FullPlaylistSerializer
|
serializer_class = FullPlaylistSerializer
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def get_queryset(self):
|
||||||
super().__init__(**kwargs)
|
qs = Playlist.objects.filter(private=False)
|
||||||
self.object = None
|
if self.request.user.is_authenticated:
|
||||||
|
qs = Playlist.objects.filter(creator=self.request.user) | qs
|
||||||
def get_object(self):
|
return qs.select_related("creator")
|
||||||
if not self.object:
|
|
||||||
self.object = super().get_object()
|
|
||||||
return self.object
|
|
||||||
|
|
||||||
|
|
||||||
class ListCreateSongAPIView(LikedSongsContextMixin, generics.ListCreateAPIView):
|
class ListCreateSongAPIView(LikedSongsContextMixin, generics.ListCreateAPIView):
|
||||||
|
@ -59,9 +75,11 @@ class ListCreateSongAPIView(LikedSongsContextMixin, generics.ListCreateAPIView):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
if self.request.user.is_authenticated:
|
if self.request.user.is_authenticated:
|
||||||
return (
|
return (
|
||||||
Song.objects.exclude(
|
Song.objects.all()
|
||||||
|
.exclude(
|
||||||
id__in=SongUserRating.objects.filter(
|
id__in=SongUserRating.objects.filter(
|
||||||
user=self.request.user
|
user=self.request.user,
|
||||||
|
like=False,
|
||||||
).values_list("song_id", flat=True)
|
).values_list("song_id", flat=True)
|
||||||
)
|
)
|
||||||
.prefetch_related("authors")
|
.prefetch_related("authors")
|
||||||
|
@ -76,29 +94,149 @@ class RetrieveUpdateDestroySongAPIView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
permission_classes = [IsCreatorOrReadOnly]
|
permission_classes = [IsCreatorOrReadOnly]
|
||||||
serializer_class = SongSerializer
|
serializer_class = SongSerializer
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def get_queryset(self):
|
||||||
super().__init__(**kwargs)
|
return Song.objects.all()
|
||||||
self.object = None
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
if not self.object:
|
class ListSongPlaylistsAPIView(generics.ListAPIView):
|
||||||
self.object = super().get_object()
|
serializer_class = ListPlaylistSerializer
|
||||||
return self.object
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Playlist.objects.filter(
|
||||||
|
songs__song__slug=self.kwargs["slug"], creator=self.request.user
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ListLikedSongsAPIView(generics.ListAPIView):
|
class ListLikedSongsAPIView(generics.ListAPIView):
|
||||||
serializer_class = ListSongSerializer
|
serializer_class = ListSongSerializer
|
||||||
pagination_class = StandardResultsSetPagination
|
pagination_class = StandardResultsSetPagination
|
||||||
authentication_classes = [permissions.IsAuthenticated]
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_serializer_context(self):
|
||||||
|
context = super().get_serializer_context()
|
||||||
|
context["likes"] = True
|
||||||
|
return context
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return (
|
return (
|
||||||
Song.objects.cache()
|
Song.objects.cache()
|
||||||
.filter(
|
.filter(
|
||||||
id__in=self.request.user.song_likes.objects.cache()
|
id__in=SongUserRating.objects.cache()
|
||||||
.all()
|
.filter(user=self.request.user, like=True)
|
||||||
.values_list("song_id", flat=True)
|
.values_list("song_id", flat=True)
|
||||||
)
|
)
|
||||||
.prefetch_related("authors")
|
.prefetch_related("authors")
|
||||||
.select_related("album")
|
.select_related("album")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ListDislikedSongsAPIView(generics.ListAPIView):
|
||||||
|
serializer_class = ListSongSerializer
|
||||||
|
pagination_class = StandardResultsSetPagination
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_serializer_context(self):
|
||||||
|
context = super().get_serializer_context()
|
||||||
|
context["likes"] = False
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return (
|
||||||
|
Song.objects.cache()
|
||||||
|
.filter(
|
||||||
|
id__in=SongUserRating.objects.cache()
|
||||||
|
.filter(user=self.request.user, like=True)
|
||||||
|
.values_list("song_id", flat=False)
|
||||||
|
)
|
||||||
|
.prefetch_related("authors")
|
||||||
|
.select_related("album")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AddSongToPlaylistAPIView(generics.CreateAPIView):
|
||||||
|
serializer_class = AddSongToPlaylistSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Playlist.objects.filter(creator=self.request.user)
|
||||||
|
|
||||||
|
def get_serializer_context(self, **kwargs):
|
||||||
|
context = super().get_serializer_context()
|
||||||
|
context["create"] = True
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveSongFromPlaylistAPIView(generics.DestroyAPIView):
|
||||||
|
serializer_class = AddSongToPlaylistSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Playlist.objects.filter(creator=self.request.user)
|
||||||
|
|
||||||
|
def get_serializer_context(self, **kwargs):
|
||||||
|
context = super().get_serializer_context()
|
||||||
|
context["create"] = False
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class LikeSongAPIView(generics.CreateAPIView):
|
||||||
|
serializer_class = LikeDislikeSongSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return SongUserRating.objects.all()
|
||||||
|
|
||||||
|
def get_serializer_context(self, **kwargs):
|
||||||
|
context = super().get_serializer_context()
|
||||||
|
context["like"] = True
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class DislikeSongAPIView(generics.CreateAPIView):
|
||||||
|
serializer_class = LikeDislikeSongSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return SongUserRating.objects.all()
|
||||||
|
|
||||||
|
def get_serializer_context(self, **kwargs):
|
||||||
|
context = super().get_serializer_context()
|
||||||
|
context["like"] = False
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class ListAlbumsAPIView(generics.ListAPIView):
|
||||||
|
serializer_class = AlbumSerializer
|
||||||
|
pagination_class = StandardResultsSetPagination
|
||||||
|
permission_classes = [permissions.AllowAny]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Album.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveUpdateDestroyAlbumAPIView(
|
||||||
|
LikedSongsContextMixin, generics.RetrieveUpdateDestroyAPIView
|
||||||
|
):
|
||||||
|
lookup_field = "slug"
|
||||||
|
lookup_url_kwarg = "slug"
|
||||||
|
permission_classes = [IsAdminOrReadOnly]
|
||||||
|
serializer_class = FullAlbumSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class ListAuthorsAPIView(generics.ListAPIView):
|
||||||
|
serializer_class = AuthorSerializer
|
||||||
|
pagination_class = StandardResultsSetPagination
|
||||||
|
permission_classes = [permissions.AllowAny]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Author.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveUpdateDestroyAuthorAPIView(
|
||||||
|
LikedSongsContextMixin, generics.RetrieveUpdateDestroyAPIView
|
||||||
|
):
|
||||||
|
lookup_field = "slug"
|
||||||
|
lookup_url_kwarg = "slug"
|
||||||
|
permission_classes = [IsAdminOrReadOnly]
|
||||||
|
serializer_class = FullAuthorSerializer
|
||||||
|
|
|
@ -87,14 +87,14 @@ class SlugMeta:
|
||||||
|
|
||||||
class Playlist(ShortLinkModel, UserHistoryModel):
|
class Playlist(ShortLinkModel, UserHistoryModel):
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
private = models.BooleanField(default=False)
|
private = models.BooleanField(default=True)
|
||||||
creator = models.ForeignKey(
|
creator = models.ForeignKey(
|
||||||
"users.User", related_name="playlists", on_delete=models.CASCADE
|
"users.User", related_name="playlists", on_delete=models.CASCADE
|
||||||
)
|
)
|
||||||
length = models.IntegerField(default=0)
|
length = models.IntegerField(default=0)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("playlist:song", kwargs={"slug": self.slug})
|
return reverse("music:playlist", kwargs={"slug": self.slug})
|
||||||
|
|
||||||
def get_songs(self):
|
def get_songs(self):
|
||||||
return self.songs.all().values("song")
|
return self.songs.all().values("song")
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from django.db.models.signals import post_delete, post_save, pre_save
|
from django.db.models.signals import post_delete, post_save, pre_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from akarpov.music.models import Song, SongUserRating
|
from akarpov.music.models import PlaylistSong, Song, SongUserRating
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Song)
|
@receiver(post_delete, sender=Song)
|
||||||
|
@ -34,3 +34,27 @@ def create_or_update_rating(sender, instance: SongUserRating, **kwargs):
|
||||||
else:
|
else:
|
||||||
song.likes -= 1
|
song.likes -= 1
|
||||||
song.save(update_fields=["likes"])
|
song.save(update_fields=["likes"])
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_delete, sender=SongUserRating)
|
||||||
|
def delete_rating(sender, instance: SongUserRating, **kwargs):
|
||||||
|
song = instance.song
|
||||||
|
if instance.like:
|
||||||
|
song.likes -= 1
|
||||||
|
else:
|
||||||
|
song.likes += 1
|
||||||
|
song.save(update_fields=["likes"])
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=PlaylistSong)
|
||||||
|
def update_playlist_length(sender, instance: PlaylistSong, **kwargs):
|
||||||
|
playlist = instance.playlist
|
||||||
|
playlist.length = playlist.songs.count()
|
||||||
|
playlist.save(update_fields=["length"])
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_delete, sender=PlaylistSong)
|
||||||
|
def update_playlist_length_delete(sender, instance: PlaylistSong, **kwargs):
|
||||||
|
playlist = instance.playlist
|
||||||
|
playlist.length = playlist.songs.count()
|
||||||
|
playlist.save(update_fields=["length"])
|
||||||
|
|
|
@ -26,6 +26,18 @@ def get_object_user(obj: Model) -> User | None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
|
def get_model_user_field(app_name: str, model: str) -> str:
|
||||||
|
model = apps.get_model(app_name, model)
|
||||||
|
if hasattr(model, "creator") and model.creator.field.related_model is User:
|
||||||
|
return "creator"
|
||||||
|
elif hasattr(model, "user") and model.user.field.related_model is User:
|
||||||
|
return "user"
|
||||||
|
elif hasattr(model, "owner") and model.owner.field.related_model is User:
|
||||||
|
return "owner"
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
@lru_cache
|
@lru_cache
|
||||||
def get_app_verbose_name(app: str) -> str:
|
def get_app_verbose_name(app: str) -> str:
|
||||||
return apps.get_app_config(app).verbose_name
|
return apps.get_app_config(app).verbose_name
|
||||||
|
|
Loading…
Reference in New Issue
Block a user