fix(views): optimize database queries to solve N+1 problems 🔨🐛

This commit is contained in:
2025-07-25 23:22:35 +05:00
parent 118ad52c71
commit 4aae0064ca
5 changed files with 39 additions and 29 deletions

View File

@@ -42,7 +42,6 @@ from .serializers import (
AuthSerializer,
CustomUserSerializer,
CustomReadOnlyUserSerializer,
CustomReadOnlyUserByIDCardSerializer,
UserProfileUpdateSerializer,
)
@@ -579,16 +578,6 @@ def filter_temporary_user(request):
)
class ListUserByIDCardView(generics.ListAPIView):
# Create user API view
permission_classes = (permissions.AllowAny,)
serializer_class = CustomReadOnlyUserByIDCardSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = "__all__"
filterset_class = UserFilter
queryset = User.objects.all()
class UserDetailAPIView(StaffEditorPermissionMixin, generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = CustomReadOnlyUserSerializer

View File

@@ -235,8 +235,11 @@ REST_FRAMEWORK = {
"login": "1000/min",
},
"EXCEPTION_HANDLER": "api.exceptions.custom_exception_handler",
"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",),
# "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema"
"DEFAULT_RENDERER_CLASSES": (
"rest_framework.renderers.JSONRenderer",
# "rest_framework.renderers.BrowsableAPIRenderer",
),
# "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}

View File

@@ -13,6 +13,7 @@ from rest_framework.response import Response
from api.mixins import StaffEditorPermissionMixin
from api.tasks import add_new_devices_to_omada
from apibase.env import BASE_DIR, env
from django.db.models import Prefetch
import logging
from .models import Device, Payment, Topup, WalletTransaction
@@ -60,10 +61,18 @@ class ListCreatePaymentView(StaffEditorPermissionMixin, generics.ListCreateAPIVi
filterset_class = PaymentFilter
def get_queryset(self):
queryset = super().get_queryset()
if self.request.user.is_superuser:
return queryset
return queryset.filter(user=self.request.user)
unpaid_qs = Payment.objects.filter(paid=False).order_by("-created_at")
device_qs = Device.objects.prefetch_related(
Prefetch("payments", queryset=unpaid_qs, to_attr="unpaid_payments")
)
queryset = Payment.objects.select_related("user").prefetch_related(
Prefetch("devices", queryset=device_qs)
)
if not self.request.user.is_superuser:
queryset = queryset.filter(user=self.request.user)
return queryset
def create(self, request):
data = request.data
@@ -163,7 +172,7 @@ class UpdatePaymentAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView):
serializer_class = PaymentSerializer
queryset = Payment.objects.all()
queryset = Payment.objects.select_related("user").all()
lookup_field = "pk"
def update(self, request, *args, **kwargs):
@@ -322,7 +331,7 @@ class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView):
class CancelPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView):
queryset = Payment.objects.all()
queryset = Payment.objects.select_related("user").all()
serializer_class = PaymentSerializer
lookup_field = "pk"
@@ -352,7 +361,7 @@ class CancelPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView):
class ListCreateTopupView(StaffEditorPermissionMixin, generics.ListCreateAPIView):
queryset = Topup.objects.all()
queryset = Topup.objects.all().prefetch_related("user")
serializer_class = TopupSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = "__all__"

View File

@@ -36,9 +36,9 @@ class DeviceSerializer(serializers.ModelSerializer):
def get_pending_payment_id(self, obj):
unpaid_payment = (
Payment.objects.filter(devices=obj, paid=False)
.order_by("-created_at")
.first()
obj.unpaid_payments[0]
if hasattr(obj, "unpaid_payments") and obj.unpaid_payments
else None
)
return unpaid_payment.id if unpaid_payment else None
@@ -63,9 +63,9 @@ class AdminDeviceSerializer(serializers.ModelSerializer):
def get_pending_payment_id(self, obj):
unpaid_payment = (
Payment.objects.filter(devices=obj, paid=False)
.order_by("-created_at")
.first()
obj.unpaid_payments[0]
if hasattr(obj, "unpaid_payments") and obj.unpaid_payments
else None
)
return unpaid_payment.id if unpaid_payment else None

View File

@@ -3,7 +3,9 @@ from xmlrpc.client import Boolean
from rest_framework import generics, status
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from billing.models import Payment
from .models import Device
from django.db.models import Prefetch
from .serializers import (
CreateDeviceSerializer,
DeviceSerializer,
@@ -28,6 +30,13 @@ class DeviceListCreateAPIView(
filterset_fields = "__all__"
filterset_class = DeviceFilter
def get_queryset(self):
unpaid_qs = Payment.objects.filter(paid=False).order_by("-created_at")
base_qs = Device.objects.select_related("user").prefetch_related(
Prefetch("payments", queryset=unpaid_qs, to_attr="unpaid_payments")
)
return base_qs.all()
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
all_devices = request.query_params.get("all_devices", "false").lower() in [
@@ -90,7 +99,7 @@ class DeviceDetailAPIView(StaffEditorPermissionMixin, generics.RetrieveAPIView):
class DeviceUpdateAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
queryset = Device.objects.all()
queryset = Device.objects.select_related("user").all()
serializer_class = CreateDeviceSerializer
lookup_field = "pk"
@@ -116,7 +125,7 @@ class DeviceUpdateAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
class DeviceBlockAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
queryset = Device.objects.all()
queryset = Device.objects.select_related("user").all()
serializer_class = BlockDeviceSerializer
lookup_field = "pk"
@@ -154,7 +163,7 @@ class DeviceBlockAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
class DeviceDestroyAPIView(StaffEditorPermissionMixin, generics.DestroyAPIView):
queryset = Device.objects.all()
queryset = Device.objects.select_related("user").all()
serializer_class = DeviceSerializer
lookup_field = "pk"