diff --git a/thenewboston/social/admin.py b/thenewboston/social/admin.py index 3e7a678..0c098f7 100644 --- a/thenewboston/social/admin.py +++ b/thenewboston/social/admin.py @@ -1,7 +1,8 @@ from django.contrib import admin -from .models import Comment, Follower, Post +from .models import Comment, Follower, Post, PostReaction admin.site.register(Comment) admin.site.register(Follower) admin.site.register(Post) +admin.site.register(PostReaction) diff --git a/thenewboston/social/migrations/0006_postreaction.py b/thenewboston/social/migrations/0006_postreaction.py new file mode 100644 index 0000000..60223b3 --- /dev/null +++ b/thenewboston/social/migrations/0006_postreaction.py @@ -0,0 +1,51 @@ +# Generated by Django 4.2.3 on 2024-08-18 13:48 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("social", "0005_follower_follower_unique_follower_following"), + ] + + operations = [ + migrations.CreateModel( + name="PostReaction", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_date", models.DateTimeField(auto_now_add=True)), + ("modified_date", models.DateTimeField(auto_now=True)), + ("reaction", models.CharField(max_length=10)), + ( + "post", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="user_reactions", + to="social.post", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/thenewboston/social/models/__init__.py b/thenewboston/social/models/__init__.py index 75e82e7..52ea4b7 100644 --- a/thenewboston/social/models/__init__.py +++ b/thenewboston/social/models/__init__.py @@ -1,3 +1,4 @@ from .comment import Comment # noqa: F401 from .follower import Follower # noqa: F401 from .post import Post # noqa: F401 +from .post_reaction import PostReaction # noqa: F401 diff --git a/thenewboston/social/models/post_reaction.py b/thenewboston/social/models/post_reaction.py new file mode 100644 index 0000000..aca25d5 --- /dev/null +++ b/thenewboston/social/models/post_reaction.py @@ -0,0 +1,13 @@ +from django.db import models + +from thenewboston.general.models import CreatedModified + + +class PostReaction(CreatedModified): + user = models.ForeignKey('users.User', on_delete=models.CASCADE) + post = models.ForeignKey('social.Post', related_name='user_reactions', on_delete=models.CASCADE) + + reaction = models.CharField(max_length=10) + + def __str__(self): + return f'{self.user}-{self.reaction}' diff --git a/thenewboston/social/serializers/post.py b/thenewboston/social/serializers/post.py index 3d4ca44..ae2ed7f 100644 --- a/thenewboston/social/serializers/post.py +++ b/thenewboston/social/serializers/post.py @@ -1,6 +1,7 @@ from rest_framework import serializers from thenewboston.general.utils.image import process_image +from thenewboston.social.serializers.post_reaction import PostReactionsReadSerializer from thenewboston.users.serializers.user import UserReadSerializer from ..models import Post @@ -10,17 +11,14 @@ class PostReadSerializer(serializers.ModelSerializer): comments = CommentReadSerializer(many=True, read_only=True) owner = UserReadSerializer(read_only=True) + user_reaction = serializers.CharField() + user_reactions = PostReactionsReadSerializer(many=True, read_only=True) class Meta: model = Post fields = ( - 'comments', - 'content', - 'created_date', - 'id', - 'image', - 'modified_date', - 'owner', + 'comments', 'content', 'created_date', 'id', 'image', 'modified_date', 'owner', 'user_reaction', + 'user_reactions' ) read_only_fields = ( 'comments', diff --git a/thenewboston/social/serializers/post_reaction.py b/thenewboston/social/serializers/post_reaction.py new file mode 100644 index 0000000..e1d6b96 --- /dev/null +++ b/thenewboston/social/serializers/post_reaction.py @@ -0,0 +1,32 @@ +from rest_framework import serializers + +from thenewboston.users.serializers.user import UserReadSerializer + +from ..models import PostReaction + + +class PostReactionsReadSerializer(serializers.ModelSerializer): + user = UserReadSerializer(read_only=True) + + class Meta: + model = PostReaction + fields = ('reaction', 'user') + read_only_fields = ( + 'user', + 'reaction', + ) + + +class PostReactionCreateUpdateSerializer(serializers.ModelSerializer): + + class Meta: + model = PostReaction + fields = ['post', 'reaction'] + + def create(self, validated_data): + post = validated_data.get('post') + reaction = validated_data.get('reaction') + user = self.context['request'].user + + post_reaction, _ = PostReaction.objects.update_or_create(user=user, post=post, defaults={'reaction': reaction}) + return post_reaction diff --git a/thenewboston/social/urls.py b/thenewboston/social/urls.py index eec63a0..df1e5a9 100644 --- a/thenewboston/social/urls.py +++ b/thenewboston/social/urls.py @@ -1,12 +1,17 @@ +from django.urls import include, path from rest_framework.routers import SimpleRouter from .views.comment import CommentViewSet from .views.follower import FollowerViewSet from .views.post import PostViewSet +from .views.post_reaction import PostReactionCreateUpdateView router = SimpleRouter(trailing_slash=False) router.register('comments', CommentViewSet) router.register('followers', FollowerViewSet) router.register('posts', PostViewSet) -urlpatterns = router.urls +urlpatterns = [ + path('', include(router.urls)), + path('post_reaction/', PostReactionCreateUpdateView.as_view(), name='post-reaction-create-update'), +] diff --git a/thenewboston/social/views/post.py b/thenewboston/social/views/post.py index 952bda9..02aa4b7 100644 --- a/thenewboston/social/views/post.py +++ b/thenewboston/social/views/post.py @@ -1,3 +1,4 @@ +from django.db.models import OuterRef, Subquery from django_filters.rest_framework import DjangoFilterBackend from rest_framework import status, viewsets from rest_framework.parsers import FormParser, MultiPartParser @@ -8,7 +9,7 @@ from thenewboston.general.permissions import IsObjectOwnerOrReadOnly from ..filters.post import PostFilter -from ..models import Post +from ..models import Post, PostReaction from ..serializers.post import PostReadSerializer, PostWriteSerializer @@ -34,6 +35,20 @@ def get_serializer_class(self): return PostReadSerializer + def get_queryset(self): + queryset = super().get_queryset() + if self.action in ['retrieve', 'list']: + print('YES: ', self.action) + + queryset = queryset.annotate( + user_reaction=Subquery( + PostReaction.objects.filter(user=self.request.user, post=OuterRef('pk') + ).values_list('reaction', flat=True)[:1] + ) + ).prefetch_related('user_reactions', 'owner') + + return queryset + def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() diff --git a/thenewboston/social/views/post_reaction.py b/thenewboston/social/views/post_reaction.py new file mode 100644 index 0000000..6e325c5 --- /dev/null +++ b/thenewboston/social/views/post_reaction.py @@ -0,0 +1,17 @@ +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.views import APIView + +from ..serializers.post_reaction import PostReactionCreateUpdateSerializer + + +class PostReactionCreateUpdateView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request, *args, **kwargs): + serializer = PostReactionCreateUpdateSerializer(data=request.data, context={'request': request}) + serializer.is_valid(raise_exception=True) + serializer.save() + + return Response(status=status.HTTP_200_OK)