Implement a custom paginator for DeviceViewSet

Running count on the annotated query for loading config_context is slow. The custom paginator removes the annotation before getting the count.
This commit is contained in:
kkthxbye-code 2022-06-13 20:45:08 +02:00
parent c81c3d11ed
commit 8ef74192ec
2 changed files with 19 additions and 1 deletions

View File

@ -19,6 +19,7 @@ from ipam.models import Prefix, VLAN
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
from netbox.api.exceptions import ServiceUnavailable from netbox.api.exceptions import ServiceUnavailable
from netbox.api.metadata import ContentTypeMetadata from netbox.api.metadata import ContentTypeMetadata
from netbox.api.pagination import StripCountAnnotationsPaginator
from netbox.api.viewsets import NetBoxModelViewSet from netbox.api.viewsets import NetBoxModelViewSet
from netbox.config import get_config from netbox.config import get_config
from utilities.api import get_serializer_for_model from utilities.api import get_serializer_for_model
@ -392,6 +393,7 @@ class DeviceViewSet(ConfigContextQuerySetMixin, NetBoxModelViewSet):
'virtual_chassis__master', 'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'tags', 'virtual_chassis__master', 'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'tags',
) )
filterset_class = filtersets.DeviceFilterSet filterset_class = filtersets.DeviceFilterSet
pagination_class = StripCountAnnotationsPaginator
def get_serializer_class(self): def get_serializer_class(self):
""" """

View File

@ -16,7 +16,7 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
def paginate_queryset(self, queryset, request, view=None): def paginate_queryset(self, queryset, request, view=None):
if isinstance(queryset, QuerySet): if isinstance(queryset, QuerySet):
self.count = queryset.count() self.count = self.get_queryset_count(queryset)
else: else:
# We're dealing with an iterable, not a QuerySet # We're dealing with an iterable, not a QuerySet
self.count = len(queryset) self.count = len(queryset)
@ -52,6 +52,9 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
return self.default_limit return self.default_limit
def get_queryset_count(self, queryset):
return queryset.count()
def get_next_link(self): def get_next_link(self):
# Pagination has been disabled # Pagination has been disabled
@ -67,3 +70,16 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
return None return None
return super().get_previous_link() return super().get_previous_link()
class StripCountAnnotationsPaginator(OptionalLimitOffsetPagination):
"""
Strips the annotations on the queryset before getting the count
to optimize pagination of complex queries.
"""
def get_queryset_count(self, queryset):
# Clone the queryset to avoid messing up the actual query
cloned_queryset = queryset.all()
cloned_queryset.query.annotations.clear()
return cloned_queryset.count()