From ec23c6eadeac8a494729353d58b66c5f2eadba7b Mon Sep 17 00:00:00 2001 From: Muhammad Abdurrehman Date: Sun, 18 Aug 2024 17:02:16 +0500 Subject: [PATCH 1/5] feat: introduce model for post reaction --- .../social/migrations/0006_postreaction.py | 43 +++++++++++++++++++ thenewboston/social/models/__init__.py | 1 + thenewboston/social/models/post_reaction.py | 11 +++++ 3 files changed, 55 insertions(+) create mode 100644 thenewboston/social/migrations/0006_postreaction.py create mode 100644 thenewboston/social/models/post_reaction.py diff --git a/thenewboston/social/migrations/0006_postreaction.py b/thenewboston/social/migrations/0006_postreaction.py new file mode 100644 index 0000000..37b8600 --- /dev/null +++ b/thenewboston/social/migrations/0006_postreaction.py @@ -0,0 +1,43 @@ +# Generated by Django 4.2.3 on 2024-08-18 11:57 + +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)), + ( + "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..f3386ad --- /dev/null +++ b/thenewboston/social/models/post_reaction.py @@ -0,0 +1,11 @@ +from django.db import models + +from thenewboston.general.models import CreatedModified + + +class PostReaction(CreatedModified): + user = models.ForeignKey('users.User', on_delete=models.CASCADE) + reaction = models.CharField(max_length=10) + + def __str__(self): + return f'{self.user}-{self.reaction}' From 9e5f1a0f2385d9d0181ce567c1b546dcebb794e0 Mon Sep 17 00:00:00 2001 From: Muhammad Abdurrehman Date: Sun, 18 Aug 2024 18:43:15 +0500 Subject: [PATCH 2/5] feat: add post in PostReaction --- thenewboston/social/migrations/0006_postreaction.py | 6 +++++- thenewboston/social/models/post_reaction.py | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/thenewboston/social/migrations/0006_postreaction.py b/thenewboston/social/migrations/0006_postreaction.py index 37b8600..5f5146a 100644 --- a/thenewboston/social/migrations/0006_postreaction.py +++ b/thenewboston/social/migrations/0006_postreaction.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2024-08-18 11:57 +# Generated by Django 4.2.3 on 2024-08-18 13:32 import django.db.models.deletion from django.conf import settings @@ -28,6 +28,10 @@ class Migration(migrations.Migration): ("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, to="social.post"), + ), ( "user", models.ForeignKey( diff --git a/thenewboston/social/models/post_reaction.py b/thenewboston/social/models/post_reaction.py index f3386ad..621fe88 100644 --- a/thenewboston/social/models/post_reaction.py +++ b/thenewboston/social/models/post_reaction.py @@ -5,6 +5,8 @@ class PostReaction(CreatedModified): user = models.ForeignKey('users.User', on_delete=models.CASCADE) + post = models.ForeignKey('social.Post', on_delete=models.CASCADE) + reaction = models.CharField(max_length=10) def __str__(self): From cab9d864c42d846dda1d9b4ebe19af8a7aebb0d1 Mon Sep 17 00:00:00 2001 From: Muhammad Abdurrehman Date: Sun, 18 Aug 2024 18:49:36 +0500 Subject: [PATCH 3/5] feat: add missing related name --- thenewboston/social/migrations/0006_postreaction.py | 8 ++++++-- thenewboston/social/models/post_reaction.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/thenewboston/social/migrations/0006_postreaction.py b/thenewboston/social/migrations/0006_postreaction.py index 5f5146a..60223b3 100644 --- a/thenewboston/social/migrations/0006_postreaction.py +++ b/thenewboston/social/migrations/0006_postreaction.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.3 on 2024-08-18 13:32 +# Generated by Django 4.2.3 on 2024-08-18 13:48 import django.db.models.deletion from django.conf import settings @@ -30,7 +30,11 @@ class Migration(migrations.Migration): ("reaction", models.CharField(max_length=10)), ( "post", - models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="social.post"), + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="user_reactions", + to="social.post", + ), ), ( "user", diff --git a/thenewboston/social/models/post_reaction.py b/thenewboston/social/models/post_reaction.py index 621fe88..aca25d5 100644 --- a/thenewboston/social/models/post_reaction.py +++ b/thenewboston/social/models/post_reaction.py @@ -5,7 +5,7 @@ class PostReaction(CreatedModified): user = models.ForeignKey('users.User', on_delete=models.CASCADE) - post = models.ForeignKey('social.Post', on_delete=models.CASCADE) + post = models.ForeignKey('social.Post', related_name='user_reactions', on_delete=models.CASCADE) reaction = models.CharField(max_length=10) From 2a10f96989588d6a387054c9d853d0f800faff44 Mon Sep 17 00:00:00 2001 From: Muhammad Abdurrehman Date: Wed, 21 Aug 2024 18:54:16 +0500 Subject: [PATCH 4/5] feat: post reactions retreive and admin registery --- thenewboston/social/admin.py | 3 ++- thenewboston/social/serializers/post.py | 12 +++++------- .../social/serializers/post_reaction.py | 17 +++++++++++++++++ thenewboston/social/views/post.py | 17 ++++++++++++++++- 4 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 thenewboston/social/serializers/post_reaction.py 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/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..099757f --- /dev/null +++ b/thenewboston/social/serializers/post_reaction.py @@ -0,0 +1,17 @@ +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', + ) 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() From cd5450a16c94723730ecfa97fa8eaf8f3fd24dd9 Mon Sep 17 00:00:00 2001 From: Muhammad Abdurrehman Date: Wed, 21 Aug 2024 19:10:44 +0500 Subject: [PATCH 5/5] feat: post reaction create/update view --- .../social/serializers/post_reaction.py | 15 +++++++++++++++ thenewboston/social/urls.py | 7 ++++++- thenewboston/social/views/post_reaction.py | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 thenewboston/social/views/post_reaction.py diff --git a/thenewboston/social/serializers/post_reaction.py b/thenewboston/social/serializers/post_reaction.py index 099757f..e1d6b96 100644 --- a/thenewboston/social/serializers/post_reaction.py +++ b/thenewboston/social/serializers/post_reaction.py @@ -15,3 +15,18 @@ class Meta: '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_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)