Skip to content

Commit 02b5d62

Browse files
committed
chore(dref): optimise active dref query
1 parent 4963628 commit 02b5d62

3 files changed

Lines changed: 56 additions & 42 deletions

File tree

assets

dref/serializers.py

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -208,15 +208,25 @@ class MiniDrefSerializer(serializers.ModelSerializer):
208208
type_of_dref_display = serializers.CharField(source="get_type_of_dref_display", read_only=True)
209209
status_display = serializers.CharField(source="get_status_display", read_only=True)
210210
country_details = MiniCountrySerializer(source="country", read_only=True)
211-
has_ops_update = serializers.SerializerMethodField()
212-
has_final_report = serializers.SerializerMethodField()
211+
starting_language = serializers.CharField(read_only=True)
212+
# Manually added fields
213213
application_type = serializers.SerializerMethodField()
214214
application_type_display = serializers.SerializerMethodField()
215-
unpublished_op_update_count = serializers.SerializerMethodField()
216-
unpublished_final_report_count = serializers.SerializerMethodField()
217-
operational_update_details = serializers.SerializerMethodField()
218-
final_report_details = serializers.SerializerMethodField()
219-
starting_language = serializers.CharField(read_only=True)
215+
# Annotate values from queryset
216+
has_ops_update = serializers.BooleanField(read_only=True)
217+
has_final_report = serializers.BooleanField(read_only=True)
218+
unpublished_op_update_count = serializers.IntegerField(read_only=True)
219+
unpublished_final_report_count = serializers.IntegerField(read_only=True)
220+
# Prefetched data
221+
operational_update_details = MiniOperationalUpdateActiveSerializer(
222+
source="prefetched_operational_updates",
223+
many=True,
224+
read_only=True,
225+
)
226+
final_report_details = MiniDrefFinalReportActiveSerializer(
227+
source="dreffinalreport",
228+
read_only=True,
229+
)
220230

221231
class Meta:
222232
model = Dref
@@ -250,39 +260,14 @@ class Meta:
250260
"starting_language",
251261
]
252262

253-
@extend_schema_field(MiniOperationalUpdateActiveSerializer(many=True))
254-
def get_operational_update_details(self, obj):
255-
queryset = DrefOperationalUpdate.objects.filter(dref_id=obj.id).order_by("-created_at")
256-
return MiniOperationalUpdateActiveSerializer(queryset, many=True).data
257-
258-
@extend_schema_field(MiniDrefFinalReportActiveSerializer)
259-
def get_final_report_details(self, obj):
260-
queryset = DrefFinalReport.objects.filter(dref_id=obj.id).first()
261-
return MiniDrefFinalReportActiveSerializer(queryset).data
262-
263-
def get_has_ops_update(self, obj) -> bool:
264-
op_count_count = obj.drefoperationalupdate_set.count()
265-
if op_count_count > 0:
266-
return True
267-
return False
268-
269-
def get_has_final_report(self, obj) -> bool:
270-
if hasattr(obj, "dreffinalreport"):
271-
return True
272-
return False
273-
274-
def get_application_type(self, obj) -> str:
263+
@extend_schema_field(serializers.CharField())
264+
def get_application_type(self, _) -> str:
275265
return "DREF"
276266

277-
def get_application_type_display(self, obj) -> str:
267+
@extend_schema_field(serializers.CharField())
268+
def get_application_type_display(self, _) -> str:
278269
return gettext("DREF application")
279270

280-
def get_unpublished_op_update_count(self, obj) -> int:
281-
return DrefOperationalUpdate.objects.filter(dref_id=obj.id).exclude(status=Dref.Status.APPROVED).count()
282-
283-
def get_unpublished_final_report_count(self, obj) -> int:
284-
return DrefFinalReport.objects.filter(dref_id=obj.id).exclude(status=Dref.Status.APPROVED).count()
285-
286271

287272
class PlannedInterventionSerializer(
288273
NestedCreateMixin,

dref/views.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
import django.utils.timezone as timezone
66
from django.contrib.auth.models import Permission
7+
from django.contrib.gis.db.models import Count, Exists, OuterRef, Q
78
from django.db import models, transaction
9+
from django.db.models.query import Prefetch
810
from django.http import HttpResponse
911
from django.templatetags.static import static
1012
from django.utils.translation import gettext
@@ -53,7 +55,7 @@
5355
logger = logging.getLogger(__name__)
5456

5557

56-
def filter_dref_queryset_by_user_access(user, queryset):
58+
def filter_dref_queryset_by_user_access(user, queryset: models.QuerySet) -> models.QuerySet[Dref]:
5759
if user.is_superuser:
5860
return queryset
5961
# Check if regional admin
@@ -344,16 +346,43 @@ class ActiveDrefOperationsViewSet(viewsets.ReadOnlyModelViewSet):
344346
serializer_class = MiniDrefSerializer
345347
permission_classes = [permissions.IsAuthenticated, DenyGuestUserPermission]
346348
filterset_class = ActiveDrefFilterSet
349+
347350
queryset = (
348-
Dref.objects.prefetch_related("planned_interventions", "needs_identified", "national_society_actions", "users")
351+
Dref.objects.select_related(
352+
"country",
353+
)
354+
.prefetch_related(
355+
Prefetch(
356+
"drefoperationalupdate_set",
357+
queryset=DrefOperationalUpdate.objects.select_related("country").order_by("-created_at"),
358+
to_attr="prefetched_operational_updates",
359+
),
360+
"dreffinalreport__country",
361+
)
349362
.order_by("-created_at")
350363
.filter(is_active=True)
351-
.distinct()
352364
)
353365

354366
def get_queryset(self):
355-
# user = self.request.user
356-
return filter_dref_queryset_by_user_access(self.request.user, super().get_queryset()).order_by("-created_at")
367+
return filter_dref_queryset_by_user_access(
368+
self.request.user,
369+
super().get_queryset(),
370+
).annotate(
371+
has_ops_update=Exists(
372+
DrefOperationalUpdate.objects.filter(dref=OuterRef("pk")),
373+
),
374+
unpublished_op_update_count=Count(
375+
"drefoperationalupdate",
376+
filter=~Q(drefoperationalupdate__status=Dref.Status.APPROVED),
377+
),
378+
has_final_report=Exists(
379+
DrefFinalReport.objects.filter(dref=OuterRef("pk")),
380+
),
381+
unpublished_final_report_count=Count(
382+
"dreffinalreport",
383+
filter=~Q(dreffinalreport__status=Dref.Status.APPROVED),
384+
),
385+
)
357386

358387

359388
class DrefShareView(views.APIView):

0 commit comments

Comments
 (0)