From 54d63255d661d6edd11c89547eaa3df929764ace Mon Sep 17 00:00:00 2001 From: Toksi Date: Wed, 24 Dec 2025 13:02:59 +0500 Subject: [PATCH] =?UTF-8?q?=D0=92=20=D1=81=D0=BF=D0=B8=D1=81=D0=BE=D0=BA?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80=D0=B0=D0=BC=D0=BC=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=B5=20=D0=BE=D1=82=D1=80=D0=B0=D0=B6=D0=B0=D1=8E=D1=89?= =?UTF-8?q?=D0=B5=D0=B5=20=D1=83=D1=87=D0=B0=D1=81=D1=82=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B0=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20=D0=B2=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B3=D1=80=D0=B0=D0=BC=D0=BC=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...erprogram_publish_projects_after_finish.py | 2 +- partner_programs/models.py | 2 +- partner_programs/serializers/programs.py | 23 ++++++++++-- partner_programs/views.py | 35 ++++++++++++------- 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/partner_programs/migrations/0015_partnerprogram_publish_projects_after_finish.py b/partner_programs/migrations/0015_partnerprogram_publish_projects_after_finish.py index f5c078f3..6ae07ac0 100644 --- a/partner_programs/migrations/0015_partnerprogram_publish_projects_after_finish.py +++ b/partner_programs/migrations/0015_partnerprogram_publish_projects_after_finish.py @@ -15,7 +15,7 @@ class Migration(migrations.Migration): name="publish_projects_after_finish", field=models.BooleanField( default=False, - help_text="Если включено, проекты участников могут стать публичными после завершения программы", + help_text="Если включено, проекты участников станут публичными после завершения программы", verbose_name="Публиковать проекты после окончания программы", ), ), diff --git a/partner_programs/models.py b/partner_programs/models.py index 333d26aa..75054e1f 100644 --- a/partner_programs/models.py +++ b/partner_programs/models.py @@ -116,7 +116,7 @@ class PartnerProgram(models.Model): publish_projects_after_finish = models.BooleanField( default=False, verbose_name="Публиковать проекты после окончания программы", - help_text="Если включено, проекты участников могут стать публичными после завершения программы", + help_text="Если включено, проекты участников станут публичными после завершения программы", ) datetime_registration_ends = models.DateTimeField( verbose_name="Дата окончания регистрации", diff --git a/partner_programs/serializers/programs.py b/partner_programs/serializers/programs.py index 325f6e71..54bed685 100644 --- a/partner_programs/serializers/programs.py +++ b/partner_programs/serializers/programs.py @@ -24,6 +24,16 @@ class PartnerProgramListSerializer(serializers.ModelSerializer): method_name="get_short_description" ) is_user_liked = serializers.SerializerMethodField(method_name="get_is_user_liked") + is_user_member = serializers.SerializerMethodField(method_name="get_is_user_member") + + def _get_user(self): + user = self.context.get("user") + if user: + return user + request = self.context.get("request") + if request: + return request.user + return None def count_likes(self, program): return get_likes_count(program) @@ -38,11 +48,19 @@ def get_short_description(self, program): def get_is_user_liked(self, obj): # fixme: copy-paste in every serializer... - user = self.context.get("user") - if user: + user = self._get_user() + if user and user.is_authenticated: return is_fan(obj, user) return False + def get_is_user_member(self, program): + if hasattr(program, "is_user_member"): + return bool(program.is_user_member) + user = self._get_user() + if not user or not user.is_authenticated: + return False + return program.users.filter(pk=user.pk).exists() + class Meta: model = PartnerProgram fields = ( @@ -60,6 +78,7 @@ class Meta: "views_count", "likes_count", "is_user_liked", + "is_user_member", ) diff --git a/partner_programs/views.py b/partner_programs/views.py index e071caa6..7e5d6634 100644 --- a/partner_programs/views.py +++ b/partner_programs/views.py @@ -4,7 +4,7 @@ from django.contrib.auth import get_user_model from django.db import IntegrityError, transaction -from django.db.models import Prefetch +from django.db.models import Exists, OuterRef, Prefetch from django.http import FileResponse from django.shortcuts import get_object_or_404 from django.utils import timezone @@ -68,19 +68,28 @@ def get_queryset(self): base_qs = super().get_queryset() participating_flag = self.request.query_params.get("participating") if not participating_flag: - return base_qs + qs = base_qs + elif not self.request.user.is_authenticated: + qs = PartnerProgram.objects.none() + else: + now = timezone.now() + qs = ( + base_qs.filter( + partner_program_profiles__user=self.request.user, + datetime_finished__gte=now, + ) + .distinct() + ) - if not self.request.user.is_authenticated: - return PartnerProgram.objects.none() + user = self.request.user + if not user.is_authenticated: + return qs - now = timezone.now() - return ( - base_qs.filter( - partner_program_profiles__user=self.request.user, - datetime_finished__gte=now, - ) - .distinct() + member_qs = PartnerProgramUserProfile.objects.filter( + partner_program=OuterRef("pk"), + user=user, ) + return qs.annotate(is_user_member=Exists(member_qs)) class PartnerProgramDetail(generics.RetrieveAPIView): @@ -108,8 +117,8 @@ def get(self, request, *args, **kwargs): class PartnerProgramProjectApplyView(GenericAPIView): """ - Создание проекта сразу в рамках программы (подать проект). - Проект создаётся как черновик и строго непубличный. + Создание проекта в рамках программы (подать проект). + Проект создаётся как непубличный черновик. """ permission_classes = [IsAuthenticated]