Skip to content

Commit 8ef7419

Browse files
committed
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.
1 parent c81c3d1 commit 8ef7419

File tree

2 files changed

+19
-1
lines changed

2 files changed

+19
-1
lines changed

netbox/dcim/api/views.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
2020
from netbox.api.exceptions import ServiceUnavailable
2121
from netbox.api.metadata import ContentTypeMetadata
22+
from netbox.api.pagination import StripCountAnnotationsPaginator
2223
from netbox.api.viewsets import NetBoxModelViewSet
2324
from netbox.config import get_config
2425
from utilities.api import get_serializer_for_model
@@ -392,6 +393,7 @@ class DeviceViewSet(ConfigContextQuerySetMixin, NetBoxModelViewSet):
392393
'virtual_chassis__master', 'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'tags',
393394
)
394395
filterset_class = filtersets.DeviceFilterSet
396+
pagination_class = StripCountAnnotationsPaginator
395397

396398
def get_serializer_class(self):
397399
"""

netbox/netbox/api/pagination.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def __init__(self):
1616
def paginate_queryset(self, queryset, request, view=None):
1717

1818
if isinstance(queryset, QuerySet):
19-
self.count = queryset.count()
19+
self.count = self.get_queryset_count(queryset)
2020
else:
2121
# We're dealing with an iterable, not a QuerySet
2222
self.count = len(queryset)
@@ -52,6 +52,9 @@ def get_limit(self, request):
5252

5353
return self.default_limit
5454

55+
def get_queryset_count(self, queryset):
56+
return queryset.count()
57+
5558
def get_next_link(self):
5659

5760
# Pagination has been disabled
@@ -67,3 +70,16 @@ def get_previous_link(self):
6770
return None
6871

6972
return super().get_previous_link()
73+
74+
75+
class StripCountAnnotationsPaginator(OptionalLimitOffsetPagination):
76+
"""
77+
Strips the annotations on the queryset before getting the count
78+
to optimize pagination of complex queries.
79+
"""
80+
def get_queryset_count(self, queryset):
81+
# Clone the queryset to avoid messing up the actual query
82+
cloned_queryset = queryset.all()
83+
cloned_queryset.query.annotations.clear()
84+
85+
return cloned_queryset.count()

0 commit comments

Comments
 (0)