Explorar el Código

initial work on config context performance improvements

John Anderson hace 5 años
padre
commit
3ba18633de

+ 12 - 1
netbox/dcim/api/views.py

@@ -340,7 +340,18 @@ class DeviceViewSet(CustomFieldModelViewSet):
     queryset = Device.objects.prefetch_related(
         'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay',
         'virtual_chassis__master', 'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'tags',
-    )
+    ).add_config_context_annotation()
+
+    #queryset = Device.objects.annotate(
+    #    config_contexts=Subquery(
+    #        ConfigContext.objects.filter(
+    #            Q(sites=OuterRef('site')) | Q(sites=None)
+    #        ).annotate(
+    #            _data=EmptyGroupByJSONBAgg('data')
+    #        ).values("_data")
+    #    )
+    #)
+
     filterset_class = filters.DeviceFilterSet
 
     def get_serializer_class(self):

+ 2 - 1
netbox/dcim/models/devices.py

@@ -15,6 +15,7 @@ from taggit.managers import TaggableManager
 from dcim.choices import *
 from dcim.constants import *
 from extras.models import ChangeLoggedModel, ConfigContextModel, CustomFieldModel, TaggedItem
+from extras.querysets import ConfigContextQuerySetMixin
 from extras.utils import extras_features
 from utilities.choices import ColorChoices
 from utilities.fields import ColorField, NaturalOrderingField
@@ -594,7 +595,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
     )
     tags = TaggableManager(through=TaggedItem)
 
-    objects = RestrictedQuerySet.as_manager()
+    objects = ConfigContextQuerySetMixin.as_manager()
 
     csv_headers = [
         'name', 'device_role', 'tenant', 'manufacturer', 'device_type', 'platform', 'serial', 'asset_tag', 'status',

+ 5 - 2
netbox/extras/models/models.py

@@ -542,8 +542,11 @@ class ConfigContextModel(models.Model):
 
         # Compile all config data, overwriting lower-weight values with higher-weight values where a collision occurs
         data = OrderedDict()
-        for context in ConfigContext.objects.get_for_object(self):
-            data = deepmerge(data, context.data)
+        #for context in ConfigContext.objects.get_for_object(self):
+        #    data = deepmerge(data, context.data)
+
+        for context in self.config_contexts:
+            data = deepmerge(data, context)
 
         # If the object has local config context data defined, merge it last
         if self.local_context_data:

+ 50 - 1
netbox/extras/querysets.py

@@ -1,6 +1,7 @@
 from collections import OrderedDict
 
-from django.db.models import Q, QuerySet
+from django.contrib.postgres.aggregates import JSONBAgg
+from django.db.models import OuterRef, Subquery, Q, QuerySet
 
 from utilities.querysets import RestrictedQuerySet
 
@@ -57,3 +58,51 @@ class ConfigContextQuerySet(RestrictedQuerySet):
             Q(tags__slug__in=obj.tags.slugs()) | Q(tags=None),
             is_active=True,
         ).order_by('weight', 'name')
+
+
+class EmptyGroupByJSONBAgg(JSONBAgg):
+    contains_aggregate = False
+
+
+class ConfigContextQuerySetMixin(RestrictedQuerySet):
+
+    def add_config_context_annotation(self):
+        from extras.models import ConfigContext
+        return self.annotate(
+            config_contexts=Subquery(
+                ConfigContext.objects.filter(
+                    self._add_config_context_filters()
+                ).order_by(
+                    'weight',
+                    'name'
+                ).annotate(
+                    _data=EmptyGroupByJSONBAgg('data')
+                ).values("_data")
+            )
+        )
+
+    def _add_config_context_filters(self):
+
+
+        if self.model._meta.model_name == 'device':
+            return Q(
+                Q(sites=OuterRef('site')) | Q(sites=None),
+                Q(roles=OuterRef('device_role')) | Q(roles=None),
+                Q(platforms=OuterRef('platform')) | Q(platforms=None),
+                Q(tenant_groups=OuterRef('tenant__group')) | Q(tenant_groups=None),
+                Q(tenants=OuterRef('tenant')) | Q(tenants=None),
+                Q(tags=OuterRef('tags')) | Q(tags=None),
+                is_active=True,
+            )
+        else:
+            return Q(
+                Q(sites=OuterRef('site')) | Q(sites=None),
+                Q(roles=OuterRef('role')) | Q(roles=None),
+                Q(platforms=OuterRef('platform')) | Q(platforms=None),
+                Q(cluster_groups=OuterRef('cluster__group')) | Q(cluster_groups=None),
+                Q(clusters=OuterRef('cluster')) | Q(clusters=None),
+                Q(tenant_groups=OuterRef('tenant__group')) | Q(tenant_groups=None),
+                Q(tenants=OuterRef('tenant')) | Q(tenants=None),
+                Q(tags=OuterRef('tags')) | Q(tags=None),
+                is_active=True,
+            )