diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 93b08a87..dffa03fa 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -45,13 +45,13 @@ from . import serializers from . import models from . import permissions from . import services +from . import utils from .votes.mixins.viewsets import LikedResourceMixin, VotersViewSetMixin ###################################################### ## Project ###################################################### - class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): queryset = models.Project.objects.all() serializer_class = serializers.ProjectDetailSerializer @@ -65,7 +65,9 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMi def get_queryset(self): qs = super().get_queryset() qs = self.attach_votes_attrs_to_queryset(qs) - return self.attach_watchers_attrs_to_queryset(qs) + qs = self.attach_watchers_attrs_to_queryset(qs) + qs = utils.attach_notify_level_to_queryset(qs, self.request.user) + return qs @detail_route(methods=["POST"]) def watch(self, request, pk=None): @@ -74,7 +76,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMi level = request.DATA.get("notify_level", None) if level is not None: set_notify_policy(notify_policy, level) - + return response @list_route(methods=["POST"]) diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index 75740a3d..627883b5 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -214,7 +214,7 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): def to_native(self, obj): #watchers is wasn't attached via the get_queryset of the viewset we need to manually add it - if not hasattr(obj, "watchers"): + if obj is not None and not hasattr(obj, "watchers"): obj.watchers = [user.id for user in services.get_watchers(obj)] return super(WatchedResourceModelSerializer, self).to_native(obj) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index f2930f55..b986e6ff 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -318,6 +318,7 @@ class ProjectSerializer(WatchersValidator, LikedResourceSerializerMixin, Watched i_am_owner = serializers.SerializerMethodField("get_i_am_owner") tags_colors = TagsColorsField(required=False) total_closed_milestones = serializers.SerializerMethodField("get_total_closed_milestones") + notify_level = serializers.IntegerField(read_only=True) class Meta: model = models.Project @@ -369,7 +370,6 @@ class ProjectDetailSerializer(ProjectSerializer): roles = ProjectRoleSerializer(source="roles", many=True, read_only=True) members = serializers.SerializerMethodField(method_name="get_members") - notify_policy = serializers.SerializerMethodField(method_name="get_notify_policy") def get_members(self, obj): qs = obj.memberships.filter(user__isnull=False) @@ -379,22 +379,6 @@ class ProjectDetailSerializer(ProjectSerializer): serializer = ProjectMemberSerializer(qs, many=True) return serializer.data - def get_notify_policy(self, obj): - request= self.context.get("request", None) - if request is None: - return None - - user = request.user - if not user.is_authenticated(): - return None - - try: - notify_policy = obj.notify_policies.get(user=user, project=obj) - return notify_policy.notify_level - - except notify_models.NotifyPolicy.DoesNotExist: - return None - class ProjectDetailAdminSerializer(ProjectDetailSerializer): class Meta: diff --git a/taiga/projects/utils.py b/taiga/projects/utils.py new file mode 100644 index 00000000..356aa769 --- /dev/null +++ b/taiga/projects/utils.py @@ -0,0 +1,40 @@ +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino +# Copyright (C) 2014 David Barragán +# Copyright (C) 2014 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.apps import apps + +def attach_notify_level_to_queryset(queryset, user, as_field="notify_level"): + """Attach notify level to each object of the queryset. + + :param queryset: A Django queryset object. + :param as_field: Attach the notify level as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + + if user.is_authenticated(): + model = queryset.model + sql = ("""SELECT notifications_notifypolicy.notify_level + FROM notifications_notifypolicy + WHERE notifications_notifypolicy.user_id = {user_id} + AND notifications_notifypolicy.project_id = {tbl}.id""") + sql = sql.format(user_id=user.id, tbl=model._meta.db_table) + else: + sql = "SELECT CAST(NULL as text)" + + qs = queryset.extra(select={as_field: sql}) + return qs