diff --git a/netbox/circuits/api/views.py b/netbox/circuits/api/views.py index 746ee02f6..32adc66ae 100644 --- a/netbox/circuits/api/views.py +++ b/netbox/circuits/api/views.py @@ -1,4 +1,4 @@ -from django.db.models import Count, Prefetch +from django.db.models import Prefetch from django.shortcuts import get_object_or_404 from rest_framework.decorators import action from rest_framework.response import Response @@ -10,6 +10,7 @@ from extras.api.serializers import RenderedGraphSerializer from extras.api.views import CustomFieldModelViewSet from extras.models import Graph from utilities.api import ModelViewSet +from utilities.utils import get_subquery from . import serializers @@ -27,8 +28,8 @@ class CircuitsRootView(APIRootView): class ProviderViewSet(CustomFieldModelViewSet): queryset = Provider.objects.prefetch_related('tags').annotate( - circuit_count=Count('circuits') - ).order_by(*Provider._meta.ordering) + circuit_count=get_subquery(Circuit, 'provider') + ) serializer_class = serializers.ProviderSerializer filterset_class = filters.ProviderFilterSet @@ -49,8 +50,8 @@ class ProviderViewSet(CustomFieldModelViewSet): class CircuitTypeViewSet(ModelViewSet): queryset = CircuitType.objects.annotate( - circuit_count=Count('circuits') - ).order_by(*CircuitType._meta.ordering) + circuit_count=get_subquery(Circuit, 'type') + ) serializer_class = serializers.CircuitTypeSerializer filterset_class = filters.CircuitTypeFilterSet diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 6c29d41f1..2722bda67 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -1,12 +1,12 @@ from django.contrib import messages from django.db import transaction -from django.db.models import Count from django.shortcuts import get_object_or_404, redirect, render from django_tables2 import RequestConfig from extras.models import Graph from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator, get_paginate_count +from utilities.utils import get_subquery from utilities.views import ( BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView, ) @@ -20,7 +20,9 @@ from .models import Circuit, CircuitTermination, CircuitType, Provider # class ProviderListView(ObjectListView): - queryset = Provider.objects.annotate(count_circuits=Count('circuits')).order_by(*Provider._meta.ordering) + queryset = Provider.objects.annotate( + count_circuits=get_subquery(Circuit, 'provider') + ) filterset = filters.ProviderFilterSet filterset_form = forms.ProviderFilterForm table = tables.ProviderTable @@ -72,14 +74,18 @@ class ProviderBulkImportView(BulkImportView): class ProviderBulkEditView(BulkEditView): - queryset = Provider.objects.annotate(count_circuits=Count('circuits')).order_by(*Provider._meta.ordering) + queryset = Provider.objects.annotate( + count_circuits=get_subquery(Circuit, 'provider') + ) filterset = filters.ProviderFilterSet table = tables.ProviderTable form = forms.ProviderBulkEditForm class ProviderBulkDeleteView(BulkDeleteView): - queryset = Provider.objects.annotate(count_circuits=Count('circuits')).order_by(*Provider._meta.ordering) + queryset = Provider.objects.annotate( + count_circuits=get_subquery(Circuit, 'provider') + ) filterset = filters.ProviderFilterSet table = tables.ProviderTable @@ -89,7 +95,9 @@ class ProviderBulkDeleteView(BulkDeleteView): # class CircuitTypeListView(ObjectListView): - queryset = CircuitType.objects.annotate(circuit_count=Count('circuits')).order_by(*CircuitType._meta.ordering) + queryset = CircuitType.objects.annotate( + circuit_count=get_subquery(Circuit, 'type') + ) table = tables.CircuitTypeTable @@ -109,7 +117,9 @@ class CircuitTypeBulkImportView(BulkImportView): class CircuitTypeBulkDeleteView(BulkDeleteView): - queryset = CircuitType.objects.annotate(circuit_count=Count('circuits')).order_by(*CircuitType._meta.ordering) + queryset = CircuitType.objects.annotate( + circuit_count=get_subquery(Circuit, 'type') + ) table = tables.CircuitTypeTable diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 2d7107ef4..0e0e074e1 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -2,7 +2,7 @@ import socket from collections import OrderedDict from django.conf import settings -from django.db.models import Count, F +from django.db.models import F from django.http import HttpResponseForbidden, HttpResponse from django.shortcuts import get_object_or_404 from drf_yasg import openapi @@ -109,7 +109,7 @@ class SiteViewSet(CustomFieldModelViewSet): vlan_count=get_subquery(VLAN, 'site'), circuit_count=get_subquery(Circuit, 'terminations__site'), virtualmachine_count=get_subquery(VirtualMachine, 'cluster__site'), - ).order_by(*Site._meta.ordering) + ) serializer_class = serializers.SiteSerializer filterset_class = filters.SiteFilterSet @@ -146,8 +146,8 @@ class RackGroupViewSet(ModelViewSet): class RackRoleViewSet(ModelViewSet): queryset = RackRole.objects.annotate( - rack_count=Count('racks') - ).order_by(*RackRole._meta.ordering) + rack_count=get_subquery(Rack, 'role') + ) serializer_class = serializers.RackRoleSerializer filterset_class = filters.RackRoleFilterSet @@ -162,7 +162,7 @@ class RackViewSet(CustomFieldModelViewSet): ).annotate( device_count=get_subquery(Device, 'rack'), powerfeed_count=get_subquery(PowerFeed, 'rack') - ).order_by(*Rack._meta.ordering) + ) serializer_class = serializers.RackSerializer filterset_class = filters.RackFilterSet @@ -237,7 +237,7 @@ class ManufacturerViewSet(ModelViewSet): devicetype_count=get_subquery(DeviceType, 'manufacturer'), inventoryitem_count=get_subquery(InventoryItem, 'manufacturer'), platform_count=get_subquery(Platform, 'manufacturer') - ).order_by(*Manufacturer._meta.ordering) + ) serializer_class = serializers.ManufacturerSerializer filterset_class = filters.ManufacturerFilterSet @@ -248,8 +248,8 @@ class ManufacturerViewSet(ModelViewSet): class DeviceTypeViewSet(CustomFieldModelViewSet): queryset = DeviceType.objects.prefetch_related('manufacturer', 'tags').annotate( - device_count=Count('instances') - ).order_by(*DeviceType._meta.ordering) + device_count=get_subquery(Device, 'device_type') + ) serializer_class = serializers.DeviceTypeSerializer filterset_class = filters.DeviceTypeFilterSet @@ -314,7 +314,7 @@ class DeviceRoleViewSet(ModelViewSet): queryset = DeviceRole.objects.annotate( device_count=get_subquery(Device, 'device_role'), virtualmachine_count=get_subquery(VirtualMachine, 'role') - ).order_by(*DeviceRole._meta.ordering) + ) serializer_class = serializers.DeviceRoleSerializer filterset_class = filters.DeviceRoleFilterSet @@ -327,7 +327,7 @@ class PlatformViewSet(ModelViewSet): queryset = Platform.objects.annotate( device_count=get_subquery(Device, 'platform'), virtualmachine_count=get_subquery(VirtualMachine, 'platform') - ).order_by(*Platform._meta.ordering) + ) serializer_class = serializers.PlatformSerializer filterset_class = filters.PlatformFilterSet @@ -619,8 +619,8 @@ class CableViewSet(ModelViewSet): class VirtualChassisViewSet(ModelViewSet): queryset = VirtualChassis.objects.prefetch_related('tags').annotate( - member_count=Count('members', distinct=True) - ).order_by(*VirtualChassis._meta.ordering) + member_count=get_subquery(Device, 'virtual_chassis') + ) serializer_class = serializers.VirtualChassisSerializer filterset_class = filters.VirtualChassisFilterSet @@ -633,8 +633,8 @@ class PowerPanelViewSet(ModelViewSet): queryset = PowerPanel.objects.prefetch_related( 'site', 'rack_group' ).annotate( - powerfeed_count=Count('powerfeeds') - ).order_by(*PowerPanel._meta.ordering) + powerfeed_count=get_subquery(PowerFeed, 'power_panel') + ) serializer_class = serializers.PowerPanelSerializer filterset_class = filters.PowerPanelFilterSet diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 42175d782..200581f4a 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -4,7 +4,7 @@ from django.contrib import messages from django.contrib.contenttypes.models import ContentType from django.core.paginator import EmptyPage, PageNotAnInteger from django.db import transaction -from django.db.models import Count, F, Prefetch +from django.db.models import F, Prefetch from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse @@ -263,7 +263,9 @@ class RackGroupBulkDeleteView(BulkDeleteView): # class RackRoleListView(ObjectListView): - queryset = RackRole.objects.annotate(rack_count=Count('racks')).order_by(*RackRole._meta.ordering) + queryset = RackRole.objects.annotate( + rack_count=get_subquery(Rack, 'role') + ) table = tables.RackRoleTable @@ -283,7 +285,9 @@ class RackRoleBulkImportView(BulkImportView): class RackRoleBulkDeleteView(BulkDeleteView): - queryset = RackRole.objects.annotate(rack_count=Count('racks')).order_by(*RackRole._meta.ordering) + queryset = RackRole.objects.annotate( + rack_count=get_subquery(Rack, 'role') + ) table = tables.RackRoleTable @@ -295,8 +299,8 @@ class RackListView(ObjectListView): queryset = Rack.objects.prefetch_related( 'site', 'group', 'tenant', 'role', 'devices__device_type' ).annotate( - device_count=Count('devices') - ).order_by(*Rack._meta.ordering) + device_count=get_subquery(Device, 'rack') + ) filterset = filters.RackFilterSet filterset_form = forms.RackFilterForm table = tables.RackDetailTable @@ -507,8 +511,8 @@ class ManufacturerBulkImportView(BulkImportView): class ManufacturerBulkDeleteView(BulkDeleteView): queryset = Manufacturer.objects.annotate( - devicetype_count=Count('device_types') - ).order_by(*Manufacturer._meta.ordering) + devicetype_count=get_subquery(DeviceType, 'manufacturer') + ) table = tables.ManufacturerTable @@ -518,8 +522,8 @@ class ManufacturerBulkDeleteView(BulkDeleteView): class DeviceTypeListView(ObjectListView): queryset = DeviceType.objects.prefetch_related('manufacturer').annotate( - instance_count=Count('instances') - ).order_by(*DeviceType._meta.ordering) + instance_count=get_subquery(Device, 'device_type') + ) filterset = filters.DeviceTypeFilterSet filterset_form = forms.DeviceTypeFilterForm table = tables.DeviceTypeTable @@ -628,8 +632,8 @@ class DeviceTypeImportView(ObjectImportView): class DeviceTypeBulkEditView(BulkEditView): queryset = DeviceType.objects.prefetch_related('manufacturer').annotate( - instance_count=Count('instances') - ).order_by(*DeviceType._meta.ordering) + instance_count=get_subquery(Device, 'device_type') + ) filterset = filters.DeviceTypeFilterSet table = tables.DeviceTypeTable form = forms.DeviceTypeBulkEditForm @@ -637,8 +641,8 @@ class DeviceTypeBulkEditView(BulkEditView): class DeviceTypeBulkDeleteView(BulkDeleteView): queryset = DeviceType.objects.prefetch_related('manufacturer').annotate( - instance_count=Count('instances') - ).order_by(*DeviceType._meta.ordering) + instance_count=get_subquery(Device, 'device_type') + ) filterset = filters.DeviceTypeFilterSet table = tables.DeviceTypeTable @@ -2198,8 +2202,8 @@ class InterfaceConnectionsListView(ObjectListView): class VirtualChassisListView(ObjectListView): queryset = VirtualChassis.objects.prefetch_related('master').annotate( - member_count=Count('members', distinct=True) - ).order_by(*VirtualChassis._meta.ordering) + member_count=get_subquery(Device, 'virtual_chassis') + ) table = tables.VirtualChassisTable filterset = filters.VirtualChassisFilterSet filterset_form = forms.VirtualChassisFilterForm @@ -2430,8 +2434,8 @@ class PowerPanelListView(ObjectListView): queryset = PowerPanel.objects.prefetch_related( 'site', 'rack_group' ).annotate( - powerfeed_count=Count('powerfeeds') - ).order_by(*PowerPanel._meta.ordering) + powerfeed_count=get_subquery(PowerFeed, 'power_panel') + ) filterset = filters.PowerPanelFilterSet filterset_form = forms.PowerPanelFilterForm table = tables.PowerPanelTable @@ -2482,8 +2486,8 @@ class PowerPanelBulkDeleteView(BulkDeleteView): queryset = PowerPanel.objects.prefetch_related( 'site', 'rack_group' ).annotate( - rack_count=Count('powerfeeds') - ).order_by(*PowerPanel._meta.ordering) + powerfeed_count=get_subquery(PowerFeed, 'power_panel') + ) filterset = filters.PowerPanelFilterSet table = tables.PowerPanelTable diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 74c9ea889..6efcc65a6 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -1,7 +1,6 @@ from collections import OrderedDict from django.contrib.contenttypes.models import ContentType -from django.db.models import Count from django.http import Http404 from django_rq.queues import get_connection from rest_framework import status @@ -15,14 +14,14 @@ from rq import Worker from extras import filters from extras.choices import JobResultStatusChoices from extras.models import ( - ConfigContext, CustomFieldChoice, ExportTemplate, Graph, ImageAttachment, ObjectChange, JobResult, Tag, + ConfigContext, CustomFieldChoice, ExportTemplate, Graph, ImageAttachment, ObjectChange, JobResult, Tag, TaggedItem, ) from extras.reports import get_report, get_reports, run_report from extras.scripts import get_script, get_scripts, run_script from utilities.api import IsAuthenticatedOrLoginNotRequired, ModelViewSet from utilities.exceptions import RQWorkerNotRunningException from utilities.metadata import ContentTypeMetadata -from utilities.utils import copy_safe_request +from utilities.utils import copy_safe_request, get_subquery from . import serializers @@ -149,8 +148,8 @@ class ExportTemplateViewSet(ModelViewSet): class TagViewSet(ModelViewSet): queryset = Tag.objects.annotate( - tagged_items=Count('extras_taggeditem_items') - ).order_by(*Tag._meta.ordering) + tagged_items=get_subquery(TaggedItem, 'tag') + ) serializer_class = serializers.TagSerializer filterset_class = filters.TagFilterSet diff --git a/netbox/extras/views.py b/netbox/extras/views.py index f2c31652c..4aa2969df 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -1,7 +1,7 @@ from django import template from django.contrib import messages from django.contrib.contenttypes.models import ContentType -from django.db.models import Count, Prefetch, Q +from django.db.models import Prefetch, Q from django.http import Http404, HttpResponseForbidden from django.shortcuts import get_object_or_404, redirect, render from django.views.generic import View @@ -13,7 +13,7 @@ from dcim.models import DeviceRole, Platform, Region, Site from tenancy.models import Tenant, TenantGroup from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator, get_paginate_count -from utilities.utils import copy_safe_request, shallow_compare_dict +from utilities.utils import copy_safe_request, get_subquery, shallow_compare_dict from utilities.views import ( BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView, ContentTypePermissionRequiredMixin, @@ -21,7 +21,7 @@ from utilities.views import ( from virtualization.models import Cluster, ClusterGroup from . import filters, forms, tables from .choices import JobResultStatusChoices -from .models import ConfigContext, ImageAttachment, ObjectChange, JobResult, Tag +from .models import ConfigContext, ImageAttachment, ObjectChange, JobResult, Tag, TaggedItem from .reports import get_report, get_reports, run_report from .scripts import get_scripts, run_script @@ -32,8 +32,8 @@ from .scripts import get_scripts, run_script class TagListView(ObjectListView): queryset = Tag.objects.annotate( - items=Count('extras_taggeditem_items') - ).order_by(*Tag._meta.ordering) + items=get_subquery(TaggedItem, 'tag') + ) filterset = filters.TagFilterSet filterset_form = forms.TagFilterForm table = tables.TagTable @@ -57,16 +57,16 @@ class TagBulkImportView(BulkImportView): class TagBulkEditView(BulkEditView): queryset = Tag.objects.annotate( - items=Count('extras_taggeditem_items') - ).order_by(*Tag._meta.ordering) + items=get_subquery(TaggedItem, 'tag') + ) table = tables.TagTable form = forms.TagBulkEditForm class TagBulkDeleteView(BulkDeleteView): queryset = Tag.objects.annotate( - items=Count('extras_taggeditem_items') - ).order_by(*Tag._meta.ordering) + items=get_subquery(TaggedItem, 'tag') + ) table = tables.TagTable diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index c296fd912..eab1af929 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -1,5 +1,4 @@ from django.conf import settings -from django.db.models import Count from django.shortcuts import get_object_or_404 from django_pglocks import advisory_lock from drf_yasg.utils import swagger_auto_schema @@ -33,7 +32,7 @@ class VRFViewSet(CustomFieldModelViewSet): queryset = VRF.objects.prefetch_related('tenant').prefetch_related('tags').annotate( ipaddress_count=get_subquery(IPAddress, 'vrf'), prefix_count=get_subquery(Prefix, 'vrf') - ).order_by(*VRF._meta.ordering) + ) serializer_class = serializers.VRFSerializer filterset_class = filters.VRFFilterSet @@ -44,8 +43,8 @@ class VRFViewSet(CustomFieldModelViewSet): class RIRViewSet(ModelViewSet): queryset = RIR.objects.annotate( - aggregate_count=Count('aggregates') - ).order_by(*RIR._meta.ordering) + aggregate_count=get_subquery(Aggregate, 'rir') + ) serializer_class = serializers.RIRSerializer filterset_class = filters.RIRFilterSet @@ -68,7 +67,7 @@ class RoleViewSet(ModelViewSet): queryset = Role.objects.annotate( prefix_count=get_subquery(Prefix, 'role'), vlan_count=get_subquery(VLAN, 'role') - ).order_by(*Role._meta.ordering) + ) serializer_class = serializers.RoleSerializer filterset_class = filters.RoleFilterSet @@ -80,7 +79,7 @@ class RoleViewSet(ModelViewSet): class PrefixViewSet(CustomFieldModelViewSet): queryset = Prefix.objects.prefetch_related( 'site', 'vrf__tenant', 'tenant', 'vlan', 'role', 'tags' - ).order_by(*Prefix._meta.ordering) + ) serializer_class = serializers.PrefixSerializer filterset_class = filters.PrefixFilterSet @@ -250,7 +249,7 @@ class PrefixViewSet(CustomFieldModelViewSet): class IPAddressViewSet(CustomFieldModelViewSet): queryset = IPAddress.objects.prefetch_related( 'vrf__tenant', 'tenant', 'nat_inside', 'nat_outside', 'tags', 'assigned_object' - ).order_by(*IPAddress._meta.ordering) + ) serializer_class = serializers.IPAddressSerializer filterset_class = filters.IPAddressFilterSet @@ -261,8 +260,8 @@ class IPAddressViewSet(CustomFieldModelViewSet): class VLANGroupViewSet(ModelViewSet): queryset = VLANGroup.objects.prefetch_related('site').annotate( - vlan_count=Count('vlans') - ).order_by(*VLANGroup._meta.ordering) + vlan_count=get_subquery(VLAN, 'group') + ) serializer_class = serializers.VLANGroupSerializer filterset_class = filters.VLANGroupFilterSet @@ -276,7 +275,7 @@ class VLANViewSet(CustomFieldModelViewSet): 'site', 'group', 'tenant', 'role', 'tags' ).annotate( prefix_count=get_subquery(Prefix, 'vlan') - ).order_by(*VLAN._meta.ordering) + ) serializer_class = serializers.VLANSerializer filterset_class = filters.VLANFilterSet diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 5ed2478ba..8dad91d09 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -1,6 +1,5 @@ import netaddr -from django.conf import settings -from django.db.models import Count, Prefetch +from django.db.models import Prefetch from django.db.models.expressions import RawSQL from django.shortcuts import get_object_or_404, redirect, render from django_tables2 import RequestConfig @@ -79,7 +78,9 @@ class VRFBulkDeleteView(BulkDeleteView): # class RIRListView(ObjectListView): - queryset = RIR.objects.annotate(aggregate_count=Count('aggregates')).order_by(*RIR._meta.ordering) + queryset = RIR.objects.annotate( + aggregate_count=get_subquery(Aggregate, 'rir') + ) filterset = filters.RIRFilterSet filterset_form = forms.RIRFilterForm table = tables.RIRDetailTable @@ -172,7 +173,9 @@ class RIRBulkImportView(BulkImportView): class RIRBulkDeleteView(BulkDeleteView): - queryset = RIR.objects.annotate(aggregate_count=Count('aggregates')).order_by(*RIR._meta.ordering) + queryset = RIR.objects.annotate( + aggregate_count=get_subquery(Aggregate, 'rir') + ) filterset = filters.RIRFilterSet table = tables.RIRTable @@ -184,7 +187,7 @@ class RIRBulkDeleteView(BulkDeleteView): class AggregateListView(ObjectListView): queryset = Aggregate.objects.prefetch_related('rir').annotate( child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) - ).order_by(*Aggregate._meta.ordering) + ) filterset = filters.AggregateFilterSet filterset_form = forms.AggregateFilterForm table = tables.AggregateDetailTable @@ -652,8 +655,8 @@ class IPAddressBulkDeleteView(BulkDeleteView): class VLANGroupListView(ObjectListView): queryset = VLANGroup.objects.prefetch_related('site').annotate( - vlan_count=Count('vlans') - ).order_by(*VLANGroup._meta.ordering) + vlan_count=get_subquery(VLAN, 'group') + ) filterset = filters.VLANGroupFilterSet filterset_form = forms.VLANGroupFilterForm table = tables.VLANGroupTable @@ -676,8 +679,8 @@ class VLANGroupBulkImportView(BulkImportView): class VLANGroupBulkDeleteView(BulkDeleteView): queryset = VLANGroup.objects.prefetch_related('site').annotate( - vlan_count=Count('vlans') - ).order_by(*VLANGroup._meta.ordering) + vlan_count=get_subquery(VLAN, 'group') + ) filterset = filters.VLANGroupFilterSet table = tables.VLANGroupTable diff --git a/netbox/netbox/views.py b/netbox/netbox/views.py index b2bee0f96..e58d214ce 100644 --- a/netbox/netbox/views.py +++ b/netbox/netbox/views.py @@ -48,8 +48,8 @@ SEARCH_TYPES = OrderedDict(( # Circuits ('provider', { 'queryset': Provider.objects.annotate( - count_circuits=Count('circuits') - ).order_by(*Provider._meta.ordering), + count_circuits=get_subquery(Circuit, 'provider') + ), 'filterset': ProviderFilterSet, 'table': ProviderTable, 'url': 'circuits:provider_list', @@ -76,17 +76,21 @@ SEARCH_TYPES = OrderedDict(( 'url': 'dcim:rack_list', }), ('rackgroup', { - 'queryset': RackGroup.objects.prefetch_related('site').annotate( - rack_count=Count('racks') - ).order_by(*RackGroup._meta.ordering), + 'queryset': RackGroup.objects.add_related_count( + RackGroup.objects.all(), + Rack, + 'group', + 'rack_count', + cumulative=True + ).prefetch_related('site'), 'filterset': RackGroupFilterSet, 'table': RackGroupTable, 'url': 'dcim:rackgroup_list', }), ('devicetype', { 'queryset': DeviceType.objects.prefetch_related('manufacturer').annotate( - instance_count=Count('instances') - ).order_by(*DeviceType._meta.ordering), + instance_count=get_subquery(Device, 'device_type') + ), 'filterset': DeviceTypeFilterSet, 'table': DeviceTypeTable, 'url': 'dcim:devicetype_list', @@ -101,8 +105,8 @@ SEARCH_TYPES = OrderedDict(( }), ('virtualchassis', { 'queryset': VirtualChassis.objects.prefetch_related('master').annotate( - member_count=Count('members', distinct=True) - ).order_by(*VirtualChassis._meta.ordering), + member_count=get_subquery(Device, 'virtual_chassis') + ), 'filterset': VirtualChassisFilterSet, 'table': VirtualChassisTable, 'url': 'dcim:virtualchassis_list', diff --git a/netbox/secrets/api/views.py b/netbox/secrets/api/views.py index 7db6f92b6..8147809b0 100644 --- a/netbox/secrets/api/views.py +++ b/netbox/secrets/api/views.py @@ -1,7 +1,6 @@ import base64 from Crypto.PublicKey import RSA -from django.db.models import Count from django.http import HttpResponseBadRequest from rest_framework.exceptions import ValidationError from rest_framework.permissions import IsAuthenticated @@ -13,6 +12,7 @@ from secrets import filters from secrets.exceptions import InvalidKey from secrets.models import Secret, SecretRole, SessionKey, UserKey from utilities.api import ModelViewSet +from utilities.utils import get_subquery from . import serializers ERR_USERKEY_MISSING = "No UserKey found for the current user." @@ -35,8 +35,8 @@ class SecretsRootView(APIRootView): class SecretRoleViewSet(ModelViewSet): queryset = SecretRole.objects.annotate( - secret_count=Count('secrets') - ).order_by(*SecretRole._meta.ordering) + secret_count=get_subquery(Secret, 'role') + ) serializer_class = serializers.SecretRoleSerializer filterset_class = filters.SecretRoleFilterSet diff --git a/netbox/secrets/views.py b/netbox/secrets/views.py index 2872616b8..e95392dfc 100644 --- a/netbox/secrets/views.py +++ b/netbox/secrets/views.py @@ -2,11 +2,11 @@ import base64 import logging from django.contrib import messages -from django.db.models import Count from django.shortcuts import get_object_or_404, redirect, render from django.utils.html import escape from django.utils.safestring import mark_safe +from utilities.utils import get_subquery from utilities.views import ( BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView, ) @@ -29,7 +29,9 @@ def get_session_key(request): # class SecretRoleListView(ObjectListView): - queryset = SecretRole.objects.annotate(secret_count=Count('secrets')).order_by(*SecretRole._meta.ordering) + queryset = SecretRole.objects.annotate( + secret_count=get_subquery(Secret, 'role') + ) table = tables.SecretRoleTable @@ -49,7 +51,9 @@ class SecretRoleBulkImportView(BulkImportView): class SecretRoleBulkDeleteView(BulkDeleteView): - queryset = SecretRole.objects.annotate(secret_count=Count('secrets')).order_by(*SecretRole._meta.ordering) + queryset = SecretRole.objects.annotate( + secret_count=get_subquery(Secret, 'role') + ) table = tables.SecretRoleTable diff --git a/netbox/virtualization/api/views.py b/netbox/virtualization/api/views.py index 55393b110..f805adb2e 100644 --- a/netbox/virtualization/api/views.py +++ b/netbox/virtualization/api/views.py @@ -1,4 +1,3 @@ -from django.db.models import Count from django.shortcuts import get_object_or_404 from rest_framework.decorators import action from rest_framework.response import Response @@ -29,16 +28,16 @@ class VirtualizationRootView(APIRootView): class ClusterTypeViewSet(ModelViewSet): queryset = ClusterType.objects.annotate( - cluster_count=Count('clusters') - ).order_by(*ClusterType._meta.ordering) + cluster_count=get_subquery(Cluster, 'type') + ) serializer_class = serializers.ClusterTypeSerializer filterset_class = filters.ClusterTypeFilterSet class ClusterGroupViewSet(ModelViewSet): queryset = ClusterGroup.objects.annotate( - cluster_count=Count('clusters') - ).order_by(*ClusterGroup._meta.ordering) + cluster_count=get_subquery(Cluster, 'group') + ) serializer_class = serializers.ClusterGroupSerializer filterset_class = filters.ClusterGroupFilterSet @@ -49,7 +48,7 @@ class ClusterViewSet(CustomFieldModelViewSet): ).annotate( device_count=get_subquery(Device, 'cluster'), virtualmachine_count=get_subquery(VirtualMachine, 'cluster') - ).order_by(*Cluster._meta.ordering) + ) serializer_class = serializers.ClusterSerializer filterset_class = filters.ClusterFilterSet diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 4cabfedeb..2b7ae3a13 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -1,6 +1,6 @@ from django.contrib import messages from django.db import transaction -from django.db.models import Count, Prefetch +from django.db.models import Prefetch from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse @@ -23,7 +23,9 @@ from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterf # class ClusterTypeListView(ObjectListView): - queryset = ClusterType.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterType._meta.ordering) + queryset = ClusterType.objects.annotate( + cluster_count=get_subquery(Cluster, 'type') + ) table = tables.ClusterTypeTable @@ -43,7 +45,9 @@ class ClusterTypeBulkImportView(BulkImportView): class ClusterTypeBulkDeleteView(BulkDeleteView): - queryset = ClusterType.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterType._meta.ordering) + queryset = ClusterType.objects.annotate( + cluster_count=get_subquery(Cluster, 'type') + ) table = tables.ClusterTypeTable @@ -52,7 +56,9 @@ class ClusterTypeBulkDeleteView(BulkDeleteView): # class ClusterGroupListView(ObjectListView): - queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterGroup._meta.ordering) + queryset = ClusterGroup.objects.annotate( + cluster_count=get_subquery(Cluster, 'group') + ) table = tables.ClusterGroupTable @@ -72,7 +78,9 @@ class ClusterGroupBulkImportView(BulkImportView): class ClusterGroupBulkDeleteView(BulkDeleteView): - queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterGroup._meta.ordering) + queryset = ClusterGroup.objects.annotate( + cluster_count=get_subquery(Cluster, 'group') + ) table = tables.ClusterGroupTable