adds vote posting
also moves some validation code to the model
This commit is contained in:
+1
-1
@@ -111,4 +111,4 @@ USE_TZ = True
|
|||||||
STATIC_URL = 'static/'
|
STATIC_URL = 'static/'
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
SESSION_COOKIE_AGE = 60*60*24*4
|
SESSION_COOKIE_AGE = 60*60*24*4
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
|
|
||||||
class Vote(models.Model):
|
class Vote(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ["user", "track"]
|
unique_together = ["user", "track"]
|
||||||
track = models.ForeignKey('Track', on_delete=models.CASCADE)
|
track = models.ForeignKey('Track', on_delete=models.CASCADE)
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
points = models.IntegerField()
|
points = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
|
||||||
skipped = models.BooleanField(default=False)
|
skipped = models.BooleanField(default=False)
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ from .artist import ArtistSerializer
|
|||||||
from .user import UserSerializer
|
from .user import UserSerializer
|
||||||
from .album import AlbumSerializer
|
from .album import AlbumSerializer
|
||||||
from .profile import ProfileSerializer, BackgroundSerializer
|
from .profile import ProfileSerializer, BackgroundSerializer
|
||||||
from .vote import VoteSerializer
|
from .vote import *
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from playlist.models import Vote
|
from playlist.models import Vote, Track
|
||||||
|
|
||||||
class VoteSerializer(serializers.HyperlinkedModelSerializer):
|
class VoteSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -10,3 +10,19 @@ class VoteSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'track': {'view_name': 'api-track', 'lookup_field': 'id'},
|
'track': {'view_name': 'api-track', 'lookup_field': 'id'},
|
||||||
'user': {'view_name': 'api-user', 'lookup_field': 'id'}
|
'user': {'view_name': 'api-user', 'lookup_field': 'id'}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VoteSaveSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Vote
|
||||||
|
fields = ['track', 'points', 'skipped']
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
"""
|
||||||
|
Create a new vote. Deletes a previous skipped vote if it exists.
|
||||||
|
"""
|
||||||
|
user = self.context['request'].user
|
||||||
|
track = validated_data['track']
|
||||||
|
previous_vote = Vote.objects.filter(user=user, track=track).first()
|
||||||
|
if previous_vote and previous_vote.skipped:
|
||||||
|
previous_vote.delete()
|
||||||
|
return Vote.objects.create(**validated_data)
|
||||||
|
|||||||
+4
-4
@@ -6,15 +6,15 @@ from django.conf.urls.static import static
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
### API
|
### API
|
||||||
path('api/', views.api_root, name="api-root"),
|
path('api/', views.api_root, name="api-root"),
|
||||||
path('api/track', views.TrackListView.as_view(), name="api-tracks"),
|
path('api/track/', views.TrackListView.as_view(), name="api-tracks"),
|
||||||
path('api/track/<id>', views.TrackDetailView.as_view(), name="api-track"),
|
path('api/track/<id>', views.TrackDetailView.as_view(), name="api-track"),
|
||||||
path('api/artist/<id>', views.ArtistDetailView.as_view(), name="api-artist"),
|
path('api/artist/<id>', views.ArtistDetailView.as_view(), name="api-artist"),
|
||||||
path('api/artist', views.ArtistListView.as_view(), name="api-artists"),
|
path('api/artist', views.ArtistListView.as_view(), name="api-artists"),
|
||||||
path('api/album/<id>', views.AlbumDetailView.as_view(), name="api-album"),
|
path('api/album/<id>', views.AlbumDetailView.as_view(), name="api-album"),
|
||||||
path('api/album', views.AlbumListView.as_view(), name="api-albums"),
|
path('api/album/', views.AlbumListView.as_view(), name="api-albums"),
|
||||||
path('api/vote', views.VoteListView.as_view(), name="api-votes"),
|
path('api/vote/', views.VoteListView.as_view(), name="api-votes"),
|
||||||
path('api/vote/<id>', views.VoteDetailView.as_view(), name="api-vote"),
|
path('api/vote/<id>', views.VoteDetailView.as_view(), name="api-vote"),
|
||||||
path('api/user', views.UserListView.as_view(), name="api-users"),
|
path('api/user/', views.UserListView.as_view(), name="api-users"),
|
||||||
path('api/user/<int:id>', views.UserDetailView.as_view(), name="api-user"),
|
path('api/user/<int:id>', views.UserDetailView.as_view(), name="api-user"),
|
||||||
path('api/user/<int:user_id>/votes', views.VoteListView.as_view(), name="api-user-votes"),
|
path('api/user/<int:user_id>/votes', views.VoteListView.as_view(), name="api-user-votes"),
|
||||||
path('api/profile/<int:id>', views.ProfileDetailView.as_view(), name="api-profile"),
|
path('api/profile/<int:id>', views.ProfileDetailView.as_view(), name="api-profile"),
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ def api_root(request, format=None):
|
|||||||
'artists': reverse('api-artists', request=request, format=format),
|
'artists': reverse('api-artists', request=request, format=format),
|
||||||
'users': reverse('api-users', request=request, format=format),
|
'users': reverse('api-users', request=request, format=format),
|
||||||
'albums': reverse('api-albums', request=request, format=format),
|
'albums': reverse('api-albums', request=request, format=format),
|
||||||
|
'votes': reverse('api-votes', request=request, format=format),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
from django.db import IntegrityError
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from playlist.models import Vote
|
from playlist.models import Vote, Profile
|
||||||
from playlist.serializers import VoteSerializer
|
from playlist.serializers import VoteSerializer, VoteSaveSerializer
|
||||||
|
|
||||||
class VoteListView(APIView):
|
class VoteListView(APIView):
|
||||||
authentication_classes = [SessionAuthentication]
|
authentication_classes = [SessionAuthentication]
|
||||||
@@ -17,6 +18,26 @@ class VoteListView(APIView):
|
|||||||
serializer = VoteSerializer(votes, many=True, context={'request': request})
|
serializer = VoteSerializer(votes, many=True, context={'request': request})
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
(profile,_) = Profile.objects.get_or_create(user=request.user)
|
||||||
|
if not profile.can_vote:
|
||||||
|
return Response({'error': 'User cannot vote'}, status=403)
|
||||||
|
serializer = VoteSaveSerializer(data=request.data, context={'request': request})
|
||||||
|
if serializer.is_valid():
|
||||||
|
try:
|
||||||
|
serializer.save(user=request.user)
|
||||||
|
return Response(serializer.data, status=201)
|
||||||
|
except IntegrityError:
|
||||||
|
return Response({'error': 'Duplicate vote'}, status=400)
|
||||||
|
return Response(serializer.errors, status=400)
|
||||||
|
|
||||||
|
def delete(self, request):
|
||||||
|
if request.user.is_superuser:
|
||||||
|
Vote.objects.all().delete()
|
||||||
|
return Response(status=204)
|
||||||
|
else:
|
||||||
|
return Response(status=403)
|
||||||
|
|
||||||
class VoteDetailView(APIView):
|
class VoteDetailView(APIView):
|
||||||
authentication_classes = [SessionAuthentication]
|
authentication_classes = [SessionAuthentication]
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|||||||
Reference in New Issue
Block a user