Selaa lähdekoodia

Merge pull request #22157 from netbox-community/22061-graphql-hints

Closes #22061: Add prefetch hints for GraphQL types
bctiemann 1 viikko sitten
vanhempi
commit
b2a1e94508

+ 2 - 2
netbox/circuits/graphql/types.py

@@ -74,7 +74,7 @@ class ProviderNetworkType(PrimaryObjectType):
 class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType):
     circuit: Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='termination')
     def termination(self) -> Annotated[
         Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]
         | Annotated['RegionType', strawberry.lazy('dcim.graphql.types')]
@@ -133,7 +133,7 @@ class CircuitGroupType(OrganizationalObjectType):
 class CircuitGroupAssignmentType(TagsMixin, BaseObjectType):
     group: Annotated['CircuitGroupType', strawberry.lazy('circuits.graphql.types')]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='member')
     def member(self) -> Annotated[
         Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]
         | Annotated['VirtualCircuitType', strawberry.lazy('circuits.graphql.types')],

+ 55 - 16
netbox/dcim/graphql/types.py

@@ -4,6 +4,7 @@ import strawberry
 import strawberry_django
 from django.db.models import Func, IntegerField
 
+from circuits.models import CircuitTermination
 from core.graphql.mixins import ChangelogMixin
 from dcim import models
 from extras.graphql.mixins import ConfigContextMixin, ContactsMixin, ImageAttachmentsMixin
@@ -17,6 +18,8 @@ from netbox.graphql.types import (
     PrimaryObjectType,
 )
 from users.graphql.mixins import OwnerMixin
+from utilities.querysets import RestrictedPrefetch
+from virtualization.models import Cluster
 
 from .filters import *
 from .mixins import CabledObjectMixin, PathEndpointMixin
@@ -288,11 +291,11 @@ class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, Prima
     inventoryitems: list[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]]
     vdcs: list[Annotated["VirtualDeviceContextType", strawberry.lazy('dcim.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='vc_master_for')
     def vc_master_for(self) -> Annotated["VirtualChassisType", strawberry.lazy('dcim.graphql.types')] | None:
         return self.vc_master_for if hasattr(self, 'vc_master_for') else None
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='parent_bay')
     def parent_bay(self) -> Annotated["DeviceBayType", strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent_bay if hasattr(self, 'parent_bay') else None
 
@@ -327,7 +330,7 @@ class InventoryItemTemplateType(ComponentTemplateType):
     role: Annotated['InventoryItemRoleType', strawberry.lazy('dcim.graphql.types')] | None
     manufacturer: Annotated['ManufacturerType', strawberry.lazy('dcim.graphql.types')]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='parent')
     def parent(self) -> Annotated['InventoryItemTemplateType', strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent
 
@@ -430,7 +433,7 @@ class FrontPortTemplateType(ModularComponentTemplateType):
 class MACAddressType(PrimaryObjectType):
     mac_address: str
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='assigned_object')
     def assigned_object(self) -> Annotated[
         Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
         | Annotated['VMInterfaceType', strawberry.lazy('virtualization.graphql.types')],
@@ -494,7 +497,7 @@ class InventoryItemType(ComponentType):
 
     child_items: list[Annotated['InventoryItemType', strawberry.lazy('dcim.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='parent')
     def parent(self) -> Annotated['InventoryItemType', strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent
 
@@ -541,11 +544,20 @@ class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, Nested
     devices: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
     children: list[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'cluster_set', info.context.request.user, 'view', queryset=Cluster.objects.all()
+        ),
+    )
     def clusters(self) -> list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
         return self.cluster_set.all()
 
-    @strawberry_django.field
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'circuit_terminations', info.context.request.user, 'view',
+            queryset=CircuitTermination.objects.all()
+        ),
+    )
     def circuit_terminations(self) -> list[
         Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
     ]:
@@ -599,7 +611,7 @@ class ModuleBayType(ModularComponentType):
     installed_module: Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')] | None
     children: list[Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='parent')
     def parent(self) -> Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent
 
@@ -864,15 +876,24 @@ class RegionType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
     sites: list[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
     children: list[Annotated["RegionType", strawberry.lazy('dcim.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='parent')
     def parent(self) -> Annotated["RegionType", strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent
 
-    @strawberry_django.field
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'cluster_set', info.context.request.user, 'view', queryset=Cluster.objects.all()
+        ),
+    )
     def clusters(self) -> list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
         return self.cluster_set.all()
 
-    @strawberry_django.field
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'circuit_terminations', info.context.request.user, 'view',
+            queryset=CircuitTermination.objects.all()
+        ),
+    )
     def circuit_terminations(self) -> list[
         Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
     ]:
@@ -903,11 +924,20 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObj
     clusters: list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]
     vlans: list[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'cluster_set', info.context.request.user, 'view', queryset=Cluster.objects.all()
+        ),
+    )
     def clusters(self) -> list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
         return self.cluster_set.all()
 
-    @strawberry_django.field
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'circuit_terminations', info.context.request.user, 'view',
+            queryset=CircuitTermination.objects.all()
+        ),
+    )
     def circuit_terminations(self) -> list[
         Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
     ]:
@@ -925,15 +955,24 @@ class SiteGroupType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
     sites: list[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
     children: list[Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='parent')
     def parent(self) -> Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent
 
-    @strawberry_django.field
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'cluster_set', info.context.request.user, 'view', queryset=Cluster.objects.all()
+        ),
+    )
     def clusters(self) -> list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
         return self.cluster_set.all()
 
-    @strawberry_django.field
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'circuit_terminations', info.context.request.user, 'view',
+            queryset=CircuitTermination.objects.all()
+        ),
+    )
     def circuit_terminations(self) -> list[
         Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
     ]:

+ 16 - 5
netbox/extras/graphql/mixins.py

@@ -4,6 +4,9 @@ import strawberry
 import strawberry_django
 from strawberry.types import Info
 
+from extras.models import ImageAttachment, JournalEntry
+from utilities.querysets import RestrictedPrefetch
+
 __all__ = (
     'ConfigContextMixin',
     'ContactsMixin',
@@ -50,16 +53,24 @@ class CustomFieldsMixin:
 @strawberry.type
 class ImageAttachmentsMixin:
 
-    @strawberry_django.field
-    def image_attachments(self, info: Info) -> list[Annotated['ImageAttachmentType', strawberry.lazy('.types')]]:
-        return self.images.restrict(info.context.request.user, 'view')
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'images', info.context.request.user, 'view', queryset=ImageAttachment.objects.all()
+        ),
+    )
+    def image_attachments(self) -> list[Annotated['ImageAttachmentType', strawberry.lazy('.types')]]:
+        return self.images.all()
 
 
 @strawberry.type
 class JournalEntriesMixin:
 
-    @strawberry_django.field
-    def journal_entries(self, info: Info) -> list[Annotated['JournalEntryType', strawberry.lazy('.types')]]:
+    @strawberry_django.field(
+        prefetch_related=lambda info: RestrictedPrefetch(
+            'journal_entries', info.context.request.user, 'view', queryset=JournalEntry.objects.all()
+        ),
+    )
+    def journal_entries(self) -> list[Annotated['JournalEntryType', strawberry.lazy('.types')]]:
         return self.journal_entries.all()
 
 

+ 6 - 6
netbox/ipam/graphql/types.py

@@ -128,7 +128,7 @@ class FHRPGroupType(IPAddressesMixin, PrimaryObjectType):
 class FHRPGroupAssignmentType(BaseObjectType):
     group: Annotated['FHRPGroupType', strawberry.lazy('ipam.graphql.types')]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='interface')
     def interface(self) -> Annotated[
         Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
         | Annotated['VMInterfaceType', strawberry.lazy('virtualization.graphql.types')],
@@ -153,7 +153,7 @@ class IPAddressType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType):
     tunnel_terminations: list[Annotated['TunnelTerminationType', strawberry.lazy('vpn.graphql.types')]]
     services: list[Annotated['ServiceType', strawberry.lazy('ipam.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='assigned_object')
     def assigned_object(self) -> Annotated[
         Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
         | Annotated['FHRPGroupType', strawberry.lazy('ipam.graphql.types')]
@@ -190,7 +190,7 @@ class PrefixType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType):
     vlan: Annotated['VLANType', strawberry.lazy('ipam.graphql.types')] | None
     role: Annotated['RoleType', strawberry.lazy('ipam.graphql.types')] | None
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='scope')
     def scope(self) -> Annotated[
         Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]
         | Annotated['RegionType', strawberry.lazy('dcim.graphql.types')]
@@ -252,7 +252,7 @@ class ServiceType(ContactsMixin, PrimaryObjectType):
     ports: list[int]
     ipaddresses: list[Annotated['IPAddressType', strawberry.lazy('ipam.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='parent')
     def parent(self) -> Annotated[
         Annotated['DeviceType', strawberry.lazy('dcim.graphql.types')]
         | Annotated['VirtualMachineType', strawberry.lazy('virtualization.graphql.types')]
@@ -291,7 +291,7 @@ class VLANType(PrimaryObjectType):
     interfaces_as_tagged: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
     vminterfaces_as_tagged: list[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='qinq_svlan')
     def qinq_svlan(self) -> Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None:
         return self.qinq_svlan
 
@@ -309,7 +309,7 @@ class VLANGroupType(OrganizationalObjectType):
     total_vlan_ids: BigInt
     tenant: Annotated['TenantType', strawberry.lazy('tenancy.graphql.types')] | None
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='scope')
     def scope(self) -> Annotated[
         Annotated['ClusterType', strawberry.lazy('virtualization.graphql.types')]
         | Annotated['ClusterGroupType', strawberry.lazy('virtualization.graphql.types')]

+ 1 - 1
netbox/virtualization/graphql/types.py

@@ -59,7 +59,7 @@ class ClusterType(ContactsMixin, VLANGroupsMixin, PrimaryObjectType):
     virtual_machines: list[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
     devices: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='scope')
     def scope(self) -> Annotated[
         Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]
         | Annotated['RegionType', strawberry.lazy('dcim.graphql.types')]

+ 1 - 1
netbox/vpn/graphql/types.py

@@ -145,7 +145,7 @@ class L2VPNType(ContactsMixin, PrimaryObjectType):
 class L2VPNTerminationType(NetBoxObjectType):
     l2vpn: Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='assigned_object')
     def assigned_object(self) -> Annotated[
         Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
         | Annotated['VLANType', strawberry.lazy('ipam.graphql.types')]

+ 1 - 1
netbox/wireless/graphql/types.py

@@ -46,7 +46,7 @@ class WirelessLANType(PrimaryObjectType):
 
     interfaces: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
 
-    @strawberry_django.field
+    @strawberry_django.field(prefetch_related='scope')
     def scope(self) -> Annotated[
         Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]
         | Annotated['RegionType', strawberry.lazy('dcim.graphql.types')]