diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 3ec77d356..0ce35aa19 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -5,7 +5,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 +from django.db.models import Count, 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 @@ -16,7 +16,7 @@ from django.views.generic import View from circuits.models import Circuit from extras.models import Graph from extras.views import ObjectConfigContextView -from ipam.models import Prefix, Service, VLAN +from ipam.models import IPAddress, Prefix, Service, VLAN from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable from secrets.models import Secret from utilities.forms import ConfirmationForm @@ -517,6 +517,7 @@ class DeviceTypeView(ObjectView): def get(self, request, pk): devicetype = get_object_or_404(self.queryset, pk=pk) + instance_count = Device.objects.restrict(request.user).filter(device_type=devicetype).count() # Component tables consoleport_table = tables.ConsolePortTemplateTable( @@ -563,6 +564,7 @@ class DeviceTypeView(ObjectView): return render(request, 'dcim/devicetype.html', { 'devicetype': devicetype, + 'instance_count': instance_count, 'consoleport_table': consoleport_table, 'consoleserverport_table': consoleserverport_table, 'powerport_table': powerport_table, @@ -987,8 +989,10 @@ class DeviceView(ObjectView): # Interfaces interfaces = device.vc_interfaces.restrict(request.user, 'view').filter(device=device).prefetch_related( + Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)), + Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)), 'lag', '_connected_interface__device', '_connected_circuittermination__circuit', 'cable', - 'cable__termination_a', 'cable__termination_b', 'ip_addresses', 'tags' + 'cable__termination_a', 'cable__termination_b', 'tags' ) # Front ports @@ -1438,7 +1442,7 @@ class InterfaceView(ObjectView): if interface.untagged_vlan is not None: vlans.append(interface.untagged_vlan) vlans[0].tagged = False - for vlan in interface.tagged_vlans.prefetch_related('site', 'group', 'tenant', 'role'): + for vlan in interface.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'): vlan.tagged = True vlans.append(vlan) vlan_table = InterfaceVLANTable( @@ -2149,13 +2153,15 @@ class VirtualChassisListView(ObjectListView): class VirtualChassisView(ObjectView): - queryset = VirtualChassis.objects.prefetch_related('members') + queryset = VirtualChassis.objects.all() def get(self, request, pk): virtualchassis = get_object_or_404(self.queryset, pk=pk) + members = Device.objects.restrict(request.user).filter(virtual_chassis=virtualchassis) return render(request, 'dcim/virtualchassis.html', { 'virtualchassis': virtualchassis, + 'members': members, }) @@ -2389,8 +2395,9 @@ class PowerPanelView(ObjectView): def get(self, request, pk): powerpanel = get_object_or_404(self.queryset, pk=pk) + power_feeds = PowerFeed.objects.restrict(request.user).filter(power_panel=powerpanel).prefetch_related('rack') powerfeed_table = tables.PowerFeedTable( - data=PowerFeed.objects.filter(power_panel=powerpanel).prefetch_related('rack'), + data=power_feeds, orderable=False ) powerfeed_table.exclude = ['power_panel'] diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 222332962..5f4a21448 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -2,13 +2,15 @@ from django import template from django.conf import settings from django.contrib import messages from django.contrib.contenttypes.models import ContentType -from django.db.models import Count, Q +from django.db.models import Count, Prefetch, Q from django.http import Http404, HttpResponseForbidden from django.shortcuts import get_object_or_404, redirect, render from django.utils.safestring import mark_safe from django.views.generic import View from django_tables2 import RequestConfig +from dcim.models import DeviceRole, Platform, Region, Site +from tenancy.models import Tenant, TenantGroup from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator from utilities.utils import shallow_compare_dict @@ -16,6 +18,7 @@ from utilities.views import ( BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView, ObjectPermissionRequiredMixin, ) +from virtualization.models import Cluster, ClusterGroup from . import filters, forms, tables from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult, Tag, TaggedItem from .reports import get_report, get_reports @@ -120,6 +123,18 @@ class ConfigContextView(ObjectView): queryset = ConfigContext.objects.all() def get(self, request, pk): + # Extend queryset to prefetch related objects + self.queryset = self.queryset.prefetch_related( + Prefetch('regions', queryset=Region.objects.restrict(request.user)), + Prefetch('sites', queryset=Site.objects.restrict(request.user)), + Prefetch('roles', queryset=DeviceRole.objects.restrict(request.user)), + Prefetch('platforms', queryset=Platform.objects.restrict(request.user)), + Prefetch('clusters', queryset=Cluster.objects.restrict(request.user)), + Prefetch('cluster_groups', queryset=ClusterGroup.objects.restrict(request.user)), + Prefetch('tenants', queryset=Tenant.objects.restrict(request.user)), + Prefetch('tenant_groups', queryset=TenantGroup.objects.restrict(request.user)), + ) + configcontext = get_object_or_404(self.queryset, pk=pk) # Determine user's preferred output format diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index ff564de6e..4fe0b6e06 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -255,7 +255,7 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel): """ Determine the prefix utilization of the aggregate and return it as a percentage. """ - queryset = Prefix.objects.filter(prefix__net_contained_or_equal=str(self.prefix)) + queryset = Prefix.objects.unrestricted().filter(prefix__net_contained_or_equal=str(self.prefix)) child_prefixes = netaddr.IPSet([p.prefix for p in queryset]) return int(float(child_prefixes.size) / self.prefix.size * 100) @@ -553,7 +553,10 @@ class Prefix(ChangeLoggedModel, CustomFieldModel): "container", calculate utilization based on child prefixes. For all others, count child IP addresses. """ if self.status == PrefixStatusChoices.STATUS_CONTAINER: - queryset = Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf) + queryset = Prefix.objects.unrestricted().filter( + prefix__net_contained=str(self.prefix), + vrf=self.vrf + ) child_prefixes = netaddr.IPSet([p.prefix for p in queryset]) return int(float(child_prefixes.size) / self.prefix.size * 100) else: diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 2d339af52..e33cb3c6e 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -1,6 +1,6 @@ import netaddr from django.conf import settings -from django.db.models import Count +from django.db.models import Count, Prefetch from django.db.models.expressions import RawSQL from django.shortcuts import get_object_or_404, redirect, render from django_tables2 import RequestConfig @@ -108,10 +108,12 @@ class RIRListView(ObjectListView): 'deprecated': 0, 'available': 0, } - aggregate_list = Aggregate.objects.filter(prefix__family=family, rir=rir) + aggregate_list = Aggregate.objects.restrict(request.user).filter(prefix__family=family, rir=rir) for aggregate in aggregate_list: - queryset = Prefix.objects.filter(prefix__net_contained_or_equal=str(aggregate.prefix)) + queryset = Prefix.objects.restrict(request.user).filter( + prefix__net_contained_or_equal=str(aggregate.prefix) + ) # Find all consumed space for each prefix status (we ignore containers for this purpose). active_prefixes = netaddr.cidr_merge( @@ -699,7 +701,9 @@ class VLANGroupVLANsView(ObjectView): def get(self, request, pk): vlan_group = get_object_or_404(self.queryset, pk=pk) - vlans = VLAN.objects.restrict(request.user, 'view').filter(group_id=pk) + vlans = VLAN.objects.restrict(request.user, 'view').filter(group_id=pk).prefetch_related( + Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user)) + ) vlans = add_available_vlans(vlan_group, vlans) vlan_table = tables.VLANDetailTable(vlans) diff --git a/netbox/templates/dcim/devicetype.html b/netbox/templates/dcim/devicetype.html index 2479d58d2..669456c10 100644 --- a/netbox/templates/dcim/devicetype.html +++ b/netbox/templates/dcim/devicetype.html @@ -131,7 +131,7 @@