Skip to content

Commit

Permalink
fix: n+1 queries
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeRomaa committed Mar 12, 2024
1 parent c6702e4 commit 48f16c5
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 81 deletions.
1 change: 1 addition & 0 deletions apps/calendar/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def get(self, request, year=datetime.now().year, month=datetime.now().month):
event_serializer = CalendarEventSerializer(events, many=True)
session_serializer = CalendarTrainingSerializer(sessions, many=True)
booking_serializer = BookingSerializer(bookings, many=True)

return Response(
{
"events": event_serializer.data,
Expand Down
1 change: 0 additions & 1 deletion apps/connections/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@
path("top/positions/", views.TopPositionsView.as_view()),
path("sessions/<int:cid>/", views.ControllerSessionsView.as_view()),
path("daily/<int:year>/", views.DailyStatisticsView.as_view()),
path("daily/<int:year>/<int:cid>/", views.UserDailyStatisticsView.as_view()),
]
12 changes: 0 additions & 12 deletions apps/connections/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,3 @@ def get(self, request, year):
connections = get_daily_statistics(year)
serializer = DailyConnectionsSerializer(connections, many=True)
return Response(serializer.data)


class UserDailyStatisticsView(APIView):
def get(self, request, year, cid):
"""
Get list of controlling hours for every
day of the given year for the given user.
"""
user = get_object_or_404(User, cid=cid)
connections = get_daily_statistics(year, user)
serializer = DailyConnectionsSerializer(connections, many=True)
return Response(serializer.data)
5 changes: 1 addition & 4 deletions apps/events/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,12 @@ class Meta:

class BasicEventSerializer(serializers.ModelSerializer):
archived = serializers.BooleanField(read_only=True, source="is_archived")
available_shifts = serializers.SerializerMethodField(read_only=True)
available_shifts = serializers.IntegerField(read_only=True)

class Meta:
model = Event
fields = ["id", "name", "banner", "start", "end", "host", "hidden", "archived", "available_shifts"]

def get_available_shifts(self, obj):
return PositionShift.objects.filter(position__event=obj, user__isnull=True).count()


class EventSerializer(serializers.ModelSerializer):
archived = serializers.BooleanField(read_only=True, source="is_archived")
Expand Down
15 changes: 13 additions & 2 deletions apps/events/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os

from discord_webhook import DiscordEmbed, DiscordWebhook
from django.db.models import Count, Q
from django.utils import timezone
from rest_framework import status
from rest_framework.exceptions import PermissionDenied
Expand Down Expand Up @@ -36,7 +37,12 @@ def get(self, request):
"""
Get list of all events.
"""
events = Event.objects.filter(end__gt=timezone.now()).order_by("start")
events = (
Event.objects.filter(end__gt=timezone.now())
.prefetch_related("positions", "positions__shifts")
.annotate(available_shifts=Count("positions__shifts", filter=Q(positions__shifts__user__isnull=True)))
.order_by("start")
)

if not (request.user.is_authenticated and request.user.is_staff):
events = events.exclude(hidden=True)
Expand Down Expand Up @@ -68,7 +74,12 @@ def get(self, request):
"""
Get list of all archived events.
"""
events = Event.objects.filter(end__lt=timezone.now()).order_by("-start")
events = (
Event.objects.filter(end__lt=timezone.now())
.prefetch_related("positions", "positions__shifts")
.annotate(available_shifts=Count("positions__shifts", filter=Q(positions__shifts__user__isnull=True)))
.order_by("-start")
)

if not (request.user.is_authenticated and request.user.is_staff):
events = events.exclude(hidden=True)
Expand Down
38 changes: 1 addition & 37 deletions apps/resources/serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.template.defaultfilters import filesizeformat
from rest_framework import serializers

from .models import Category, Resource
from .models import Resource


class ResourceSerializer(serializers.ModelSerializer):
Expand All @@ -17,39 +17,3 @@ def get_extension(self, resource):

def get_size(self, resource):
return filesizeformat(resource.size)


class ResourceGroupedSerializer(serializers.ModelSerializer):
poly = serializers.SerializerMethodField()
proc = serializers.SerializerMethodField()
loa = serializers.SerializerMethodField()
vatis = serializers.SerializerMethodField()
rvm = serializers.SerializerMethodField()
ref = serializers.SerializerMethodField()

def _build_resource_list(self, category):
resources = Resource.objects.filter(category=category)
serializer = ResourceSerializer(resources, many=True)
return serializer.data

def get_poly(self, obj):
return self._build_resource_list(Category.POLY)

def get_proc(self, obj):
return self._build_resource_list(Category.PROC)

def get_loa(self, obj):
return self._build_resource_list(Category.LOA)

def get_vatis(self, obj):
return self._build_resource_list(Category.VATIS)

def get_rvm(self, obj):
return self._build_resource_list(Category.RVM)

def get_ref(self, obj):
return self._build_resource_list(Category.REF)

class Meta:
model = Resource
fields = ["poly", "proc", "loa", "vatis", "rvm", "ref"]
18 changes: 14 additions & 4 deletions apps/resources/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from collections import defaultdict

from rest_framework import status
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
Expand All @@ -6,7 +8,7 @@
from zhu_core.permissions import IsGet, IsStaff

from .models import Resource
from .serializers import ResourceGroupedSerializer, ResourceSerializer
from .serializers import ResourceSerializer


class ResourceListView(APIView):
Expand All @@ -16,9 +18,17 @@ def get(self, request):
"""
Get list of all resources.
"""
resources = Resource.objects.all()
serializer = ResourceGroupedSerializer(resources)
return Response(serializer.data)
sorted_resources = defaultdict(list)

for resource in Resource.objects.all():
sorted_resources[resource.category].append(resource)

return Response(
{
category.lower(): ResourceSerializer(resources, many=True).data
for category, resources in sorted_resources.items()
}
)

def post(self, request):
"""
Expand Down
8 changes: 7 additions & 1 deletion apps/training/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,17 @@ class TrainingSessionSerializer(BaseTrainingSessionSerializer):


class BaseTrainingRequestSerializer(serializers.ModelSerializer):
class Meta:
model = TrainingRequest
fields = ["id", "start", "end", "type", "level", "remarks"]


class TrainingRequestSerializer(BaseTrainingRequestSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), default=serializers.CurrentUserDefault())

class Meta:
model = TrainingRequest
fields = ["id", "user", "start", "end", "type", "level", "remarks"]
fields = BaseTrainingRequestSerializer.Meta.fields + ["user"]

def validate(self, data):
if data.get("user").training_requests.filter(end__gt=timezone.now()).count() >= 7:
Expand Down
53 changes: 33 additions & 20 deletions apps/training/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from collections import defaultdict

from django.db.models import Max, Q
from django.shortcuts import get_object_or_404
from django.template.loader import render_to_string
from django.utils import timezone
Expand All @@ -12,9 +15,9 @@
from .models import DayOfWeek, MentorAvailability, Status, TrainingRequest, TrainingSession
from .serializers import (
BaseTrainingRequestSerializer,
BaseTrainingSessionSerializer,
BasicMentorAvailabilitySerializer,
MentorAvailabilitySerializer,
TrainingRequestSerializer,
TrainingSessionSerializer,
)

Expand Down Expand Up @@ -60,7 +63,7 @@ def post(self, request, session_id):
"""
session = get_object_or_404(TrainingSession, id=session_id)
request.data["status"] = Status.COMPLETED
serializer = BaseTrainingSessionSerializer(session, data=request.data, partial=True)
serializer = TrainingRequestSerializer(session, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()

Expand All @@ -80,7 +83,7 @@ def put(self, request, session_id):
Modify training session details.
"""
session = get_object_or_404(TrainingSession, id=session_id)
serializer = BaseTrainingSessionSerializer(session, data=request.data, partial=True)
serializer = TrainingRequestSerializer(session, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
Expand Down Expand Up @@ -113,42 +116,52 @@ def get(self, request):
Get list of own pending training requests.
"""
requests = TrainingRequest.objects.filter(user=request.user, end__gt=timezone.now())
serializer = BaseTrainingRequestSerializer(requests, many=True)
serializer = TrainingRequestSerializer(requests, many=True)
return Response(data=serializer.data)

def post(self, request):
"""
Submit a new training request.
"""
serializer = BaseTrainingRequestSerializer(data=request.data, context={"request": request})
serializer = TrainingRequestSerializer(data=request.data, context={"request": request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class PendingTrainingRequestListView(APIView):
permission_classes = [IsTrainingStaff]
# permission_classes = [IsTrainingStaff]

def get(self, request):
"""
Get list of all pending training requests.
"""
requests = TrainingRequest.objects.filter(end__gt=timezone.now())
data = []
for cid in requests.values_list("user", flat=True).distinct():
last_session = (
TrainingSession.objects.filter(student=cid, status=Status.COMPLETED).order_by("-start").first()
requests = (
TrainingRequest.objects.select_related("user")
.annotate(
last_session=Max(
"user__student_sessions__start",
filter=Q(user__student_sessions__status=Status.COMPLETED),
)
)
user_requests = requests.filter(user=cid)
data.append(
.filter(end__gt=timezone.now())
)

sorted_requests = defaultdict(list)

for request in requests:
sorted_requests[request.user.cid].append(request)

return Response(
[
{
"user": BasicUserSerializer(user_requests[0].user).data,
"requests": BaseTrainingRequestSerializer(user_requests, many=True).data,
"last_session": last_session.start.isoformat() if last_session else None,
}
)
return Response(data=data)
"user": BasicUserSerializer(requests[0].user).data,
"requests": BaseTrainingRequestSerializer(requests, many=True).data,
"last_session": requests[0].last_session,
} for requests in sorted_requests.values()
]
)


class TrainingRequestInstanceView(APIView):
Expand All @@ -164,7 +177,7 @@ def put(self, request, request_id):
Accept training request.
"""
training_request = get_object_or_404(TrainingRequest, id=request_id)
serializer = BaseTrainingSessionSerializer(
serializer = TrainingRequestSerializer(
data={"student": training_request.user.cid, **request.data}, context={"request": request}
)
if serializer.is_valid():
Expand Down

0 comments on commit 48f16c5

Please sign in to comment.