From 524367ae0b028bbdcaad8e051d9a3ec77deae34e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BD=D0=B8=D1=86=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD?= Date: Sun, 9 Jul 2023 15:15:17 +0500 Subject: [PATCH 1/9] reactions --- src/chats/models.py | 7 ++++--- src/profiles/models.py | 6 ++---- src/profiles/views.py | 4 +++- src/reactions/mixins.py | 34 ++++++++++++++++++++++++++++++++++ src/reactions/models.py | 10 ++++++---- src/reactions/serializers.py | 21 ++++++++++++++++++--- src/reactions/services.py | 28 ++++++++++++++++++++++++++++ src/reactions/views.py | 6 +++--- 8 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 src/reactions/mixins.py create mode 100644 src/reactions/services.py diff --git a/src/chats/models.py b/src/chats/models.py index 52bd67a..f2b0a84 100644 --- a/src/chats/models.py +++ b/src/chats/models.py @@ -5,9 +5,11 @@ TimeCreateUpdateModel, AccountForeignModel, ) - +from django.contrib.contenttypes.fields import GenericRelation # TODO: change folder path + + def get_group_preview_file_path(instance, *_, **__) -> str: return instance.created.strftime("uploads/chat_previews/%Y/%m/%d/") + str(instance.id) # type: ignore @@ -125,8 +127,7 @@ class Message(UUIDModel, TimeCreateModel, AccountForeignModel): upload_to=get_message_img_file_path, null=True ) # TODO: added extra models FK viewed = models.BooleanField(default=False) - reactions = models.ManyToManyField("reactions.Reaction", related_name="messages") - + reactions = GenericRelation("reactions.ReactionLike") objects = models.Manager() message_manager = MessageManager() diff --git a/src/profiles/models.py b/src/profiles/models.py index 758c99f..08e68e8 100644 --- a/src/profiles/models.py +++ b/src/profiles/models.py @@ -1,11 +1,10 @@ from typing import Optional - +from django.contrib.contenttypes.fields import GenericRelation from django.db import models from django.urls import reverse from django.conf import settings from service.models import UUIDModel, TimeCreateUpdateModel, AccountOneToOneModel - # choices for Profile.sex field SEX_CHOICES = (("Не выбран", "Не выбран"), ("Мужской", "Мужской"), ("Женский", "Женский"), ("Другой", "Другой")) @@ -74,8 +73,7 @@ class Post(UUIDModel, TimeCreateUpdateModel): # type: ignore photo = models.ImageField( upload_to="uploads/post_img/", verbose_name="Фото", blank=True, null=True ) # TODO add table Post Images - reactions = models.ManyToManyField("reactions.Reaction", related_name="posts") - + reactions = GenericRelation("reactions.ReactionLike") objects = models.Manager() post_manager = PostManager() diff --git a/src/profiles/views.py b/src/profiles/views.py index da8c7c8..d36b851 100644 --- a/src/profiles/views.py +++ b/src/profiles/views.py @@ -9,6 +9,7 @@ PostDetailSerializer, PostListSerializer, PostCreateSerializer ) from .permissions import IsProfileOwner, IsPostOwner +from reactions.mixins import ReactionsdMixin class ProfileViewSet(mixins.ListModelMixin, @@ -52,7 +53,8 @@ def posts(self, request, username): return Response(serializer.data) -class PostViewSet(mixins.RetrieveModelMixin, +class PostViewSet(ReactionsdMixin, + mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, diff --git a/src/reactions/mixins.py b/src/reactions/mixins.py new file mode 100644 index 0000000..142adae --- /dev/null +++ b/src/reactions/mixins.py @@ -0,0 +1,34 @@ +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import AllowAny + +from reactions.serializers import FanSerializer +from reactions.services import add_like, remove_like, get_fans + + +class ReactionsdMixin: + @action(detail=True, methods=['post'], + url_path='like/(P[a-z0-9]+)', + permission_classes=[AllowAny]) + def like(self, request, pk=None, reaction_pk=None): + obj = self.get_object() + reaction_id = self.kwargs['reaction_pk'] + add_like(obj, request.user, reaction_id) + + return Response() + + @action(detail=True, methods=['delete'], url_name='unlike', + url_path='unlike/(?P[a-z0-9]+)', + permission_classes=[AllowAny]) + def unlike(self, request, pk=None, reaction_pk=None): + obj = self.get_object() + reaction_id = self.kwargs['reaction_pk'] + remove_like(obj, request.user, reaction_id) + return Response() + + @action(detail=True, methods=['get'], ) + def get_fans(self, request, pk=None, reaction_pk=None, permission_classes=[AllowAny]): + obj = self.get_object() + fans = get_fans(obj, request.user) + serializer = FanSerializer(fans, many=True) + return Response(serializer.data) diff --git a/src/reactions/models.py b/src/reactions/models.py index e036f4d..76711c2 100644 --- a/src/reactions/models.py +++ b/src/reactions/models.py @@ -1,10 +1,11 @@ from django.db import models - +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType from users.models import Account from service.models import UUIDModel -class Reaction(UUIDModel): +class ReactionLike(UUIDModel): owner = models.ForeignKey( Account, on_delete=models.PROTECT, @@ -16,8 +17,9 @@ class Reaction(UUIDModel): on_delete=models.CASCADE, verbose_name="Варианты реакций из определенного списка", ) - - objects = models.Manager() + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey('content_type', 'object_id') class Meta: verbose_name = "Реакция пользователя на пост/сообщение" diff --git a/src/reactions/serializers.py b/src/reactions/serializers.py index 66ee48c..ffd4e7e 100644 --- a/src/reactions/serializers.py +++ b/src/reactions/serializers.py @@ -1,8 +1,8 @@ from rest_framework import serializers from rest_framework.serializers import ModelSerializer -from .fields import Base64ImageField -from .models import Reaction, ReactionItem +from reactions.fields import Base64ImageField +from reactions.models import ReactionLike, ReactionItem class ReactionItemSerializer(ModelSerializer): @@ -18,5 +18,20 @@ class ReactionSerializer(ModelSerializer): owner = serializers.StringRelatedField(many=True, read_only=True) class Meta: - model = Reaction + model = ReactionLike fields = ('id', 'reaction_option', 'owner') + + +class FanSerializer(serializers.ModelSerializer): + content_type = serializers.CharField(source="content_type.name") + content_object = serializers.CharField(source="content_object.id") + + class Meta: + model = ReactionLike + fields = ( + 'id', + 'owner ', + 'content_type', + 'content_object', + 'ReactionItem' + ) diff --git a/src/reactions/services.py b/src/reactions/services.py new file mode 100644 index 0000000..062c1a2 --- /dev/null +++ b/src/reactions/services.py @@ -0,0 +1,28 @@ +from django.contrib.contenttypes.models import ContentType +from reactions.models import ReactionLike + + +def add_like(obj, user, reaction_id=None): + obj_type = ContentType.objects.get_for_model(obj) + like = ReactionLike.objects.get_or_create(content_type=obj_type, + object_id=obj.id, user=user, reaction=reaction_id) + + return like + + +def remove_like(obj, user, reaction_id=None): + obj_type = ContentType.objects.get_for_model(obj) + ReactionLike.objects.filter(content_type=obj_type, object_id=obj.id, + user=user, reaction=reaction_id).delete() + + +def is_fan(obj, user) -> bool: + obj_type = ContentType.objects.get_for_model(obj) + likes = ReactionLike.objects.filter(content_type=obj_type, + object_id=obj.id, user=user) + + return likes.exists() + + +def get_fans(obj, user): + return ReactionLike.objects.filter(object_id=obj.id, user=user) diff --git a/src/reactions/views.py b/src/reactions/views.py index 352f890..e1046d7 100644 --- a/src/reactions/views.py +++ b/src/reactions/views.py @@ -1,8 +1,8 @@ from rest_framework import mixins, viewsets from rest_framework.permissions import IsAdminUser, IsAuthenticated -from .models import Reaction, ReactionItem -from .serializers import ReactionItemSerializer, ReactionSerializer +from reactions.models import ReactionLike, ReactionItem +from reactions.serializers import ReactionItemSerializer, ReactionSerializer class ReactionItemViewSet(viewsets.ModelViewSet): @@ -13,6 +13,6 @@ class ReactionItemViewSet(viewsets.ModelViewSet): class ReactionViewSet(mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): - queryset = Reaction.objects.all() + queryset = ReactionLike.objects.all() serializer_class = ReactionSerializer permission_classes = [IsAuthenticated] From a6e54ce7ef6b63e019ef56ce7dc18f594855c572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BD=D0=B8=D1=86=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD?= Date: Sun, 9 Jul 2023 15:23:07 +0500 Subject: [PATCH 2/9] reactions --- src/reactions/mixins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reactions/mixins.py b/src/reactions/mixins.py index 142adae..7e9b697 100644 --- a/src/reactions/mixins.py +++ b/src/reactions/mixins.py @@ -27,7 +27,7 @@ def unlike(self, request, pk=None, reaction_pk=None): return Response() @action(detail=True, methods=['get'], ) - def get_fans(self, request, pk=None, reaction_pk=None, permission_classes=[AllowAny]): + def get_fans(self, request, pk=None, reaction_pk=None, permission_classes=[AllowAny]): obj = self.get_object() fans = get_fans(obj, request.user) serializer = FanSerializer(fans, many=True) From 0b2cfb54aba0518719d513c9b4044159541f24ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BD=D0=B8=D1=86=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD?= Date: Sun, 9 Jul 2023 15:27:47 +0500 Subject: [PATCH 3/9] reactions --- src/reactions/services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reactions/services.py b/src/reactions/services.py index 062c1a2..24bf7dc 100644 --- a/src/reactions/services.py +++ b/src/reactions/services.py @@ -16,7 +16,7 @@ def remove_like(obj, user, reaction_id=None): user=user, reaction=reaction_id).delete() -def is_fan(obj, user) -> bool: +def is_fan(obj, user): obj_type = ContentType.objects.get_for_model(obj) likes = ReactionLike.objects.filter(content_type=obj_type, object_id=obj.id, user=user) From 59d0d6ebe4f76393e6cb6720ac856acc32e84114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BD=D0=B8=D1=86=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD?= Date: Sun, 9 Jul 2023 15:34:28 +0500 Subject: [PATCH 4/9] reactions --- src/chats/models.py | 1 + src/profiles/models.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/chats/models.py b/src/chats/models.py index f2b0a84..849e90d 100644 --- a/src/chats/models.py +++ b/src/chats/models.py @@ -128,6 +128,7 @@ class Message(UUIDModel, TimeCreateModel, AccountForeignModel): ) # TODO: added extra models FK viewed = models.BooleanField(default=False) reactions = GenericRelation("reactions.ReactionLike") + like = GenericRelation("reactions.ReactionLike") objects = models.Manager() message_manager = MessageManager() diff --git a/src/profiles/models.py b/src/profiles/models.py index 08e68e8..1b99dcf 100644 --- a/src/profiles/models.py +++ b/src/profiles/models.py @@ -73,7 +73,8 @@ class Post(UUIDModel, TimeCreateUpdateModel): # type: ignore photo = models.ImageField( upload_to="uploads/post_img/", verbose_name="Фото", blank=True, null=True ) # TODO add table Post Images - reactions = GenericRelation("reactions.ReactionLike") + reactions = models.ManyToManyField("reactions.Reaction", related_name="posts") + like = GenericRelation("reactions.ReactionLike") objects = models.Manager() post_manager = PostManager() From f2e472891f6e0c740a7390b01e123034fd1a76aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BD=D0=B8=D1=86=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD?= Date: Sun, 9 Jul 2023 15:40:29 +0500 Subject: [PATCH 5/9] reactions --- src/chats/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chats/models.py b/src/chats/models.py index 849e90d..f956a9b 100644 --- a/src/chats/models.py +++ b/src/chats/models.py @@ -127,7 +127,7 @@ class Message(UUIDModel, TimeCreateModel, AccountForeignModel): upload_to=get_message_img_file_path, null=True ) # TODO: added extra models FK viewed = models.BooleanField(default=False) - reactions = GenericRelation("reactions.ReactionLike") + reactions = models.ManyToManyField("reactions.Reaction", related_name="messages") like = GenericRelation("reactions.ReactionLike") objects = models.Manager() message_manager = MessageManager() From ddd2a90910bb873810a80b460ce10d09840edc31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BD=D0=B8=D1=86=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD?= Date: Sun, 9 Jul 2023 15:45:33 +0500 Subject: [PATCH 6/9] reactions --- src/chats/models.py | 2 +- src/profiles/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chats/models.py b/src/chats/models.py index f956a9b..47d9140 100644 --- a/src/chats/models.py +++ b/src/chats/models.py @@ -127,7 +127,7 @@ class Message(UUIDModel, TimeCreateModel, AccountForeignModel): upload_to=get_message_img_file_path, null=True ) # TODO: added extra models FK viewed = models.BooleanField(default=False) - reactions = models.ManyToManyField("reactions.Reaction", related_name="messages") + reactions = models.ManyToManyField("reactions.ReactionLike", related_name="messages") like = GenericRelation("reactions.ReactionLike") objects = models.Manager() message_manager = MessageManager() diff --git a/src/profiles/models.py b/src/profiles/models.py index 1b99dcf..3432362 100644 --- a/src/profiles/models.py +++ b/src/profiles/models.py @@ -73,7 +73,7 @@ class Post(UUIDModel, TimeCreateUpdateModel): # type: ignore photo = models.ImageField( upload_to="uploads/post_img/", verbose_name="Фото", blank=True, null=True ) # TODO add table Post Images - reactions = models.ManyToManyField("reactions.Reaction", related_name="posts") + reactions = models.ManyToManyField("reactions.ReactionLike", related_name="posts") like = GenericRelation("reactions.ReactionLike") objects = models.Manager() post_manager = PostManager() From 44d78e814474b9cec53c74bb25f459bcd5a601c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BD=D0=B8=D1=86=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD?= Date: Sun, 9 Jul 2023 15:58:16 +0500 Subject: [PATCH 7/9] reactions --- src/profiles/views.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/profiles/views.py b/src/profiles/views.py index d36b851..5258c89 100644 --- a/src/profiles/views.py +++ b/src/profiles/views.py @@ -9,7 +9,6 @@ PostDetailSerializer, PostListSerializer, PostCreateSerializer ) from .permissions import IsProfileOwner, IsPostOwner -from reactions.mixins import ReactionsdMixin class ProfileViewSet(mixins.ListModelMixin, @@ -53,12 +52,11 @@ def posts(self, request, username): return Response(serializer.data) -class PostViewSet(ReactionsdMixin, - mixins.RetrieveModelMixin, +class PostViewSet(mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, - viewsets.GenericViewSet): + viewsets.GenericViewSet): queryset = Post.objects.all() lookup_url_kwarg = "id" lookup_field = "id" From 0fbe5076bb860ddd577cd97ff8e04ab966de4c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BD=D0=B8=D1=86=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD?= Date: Sun, 9 Jul 2023 16:01:22 +0500 Subject: [PATCH 8/9] reactions --- src/profiles/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/profiles/views.py b/src/profiles/views.py index 5258c89..da8c7c8 100644 --- a/src/profiles/views.py +++ b/src/profiles/views.py @@ -56,7 +56,7 @@ class PostViewSet(mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, - viewsets.GenericViewSet): + viewsets.GenericViewSet): queryset = Post.objects.all() lookup_url_kwarg = "id" lookup_field = "id" From 2b060695b35b0863ad67cca08a0cd464035e3c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BD=D0=B8=D1=86=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD?= Date: Sun, 9 Jul 2023 16:05:09 +0500 Subject: [PATCH 9/9] reactions --- src/profiles/models.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/profiles/models.py b/src/profiles/models.py index 3432362..0583aca 100644 --- a/src/profiles/models.py +++ b/src/profiles/models.py @@ -1,5 +1,4 @@ from typing import Optional -from django.contrib.contenttypes.fields import GenericRelation from django.db import models from django.urls import reverse from django.conf import settings @@ -74,7 +73,6 @@ class Post(UUIDModel, TimeCreateUpdateModel): # type: ignore upload_to="uploads/post_img/", verbose_name="Фото", blank=True, null=True ) # TODO add table Post Images reactions = models.ManyToManyField("reactions.ReactionLike", related_name="posts") - like = GenericRelation("reactions.ReactionLike") objects = models.Manager() post_manager = PostManager()