1010from calendar import monthrange
1111from collections .abc import Callable
1212from datetime import date , datetime , timedelta
13+ from functools import cached_property
1314from math import pi , sqrt
1415from pathlib import Path
1516
@@ -1303,52 +1304,77 @@ def get_celery_worker_status():
13031304
13041305
13051306# Used to display the counts and enabled tabs in the product view
1307+ # Uses @cached_property for lazy loading to avoid expensive queries on every page load
1308+ # See: https://github.com/DefectDojo/django-DefectDojo/issues/10313
13061309class Product_Tab :
13071310 def __init__ (self , product , title = None , tab = None ):
1308- self .product = product
1309- self .title = title
1310- self .tab = tab
1311- self .engagement_count = Engagement .objects .filter (
1312- product = self .product , active = True ).count ()
1313- self .open_findings_count = Finding .objects .filter (test__engagement__product = self .product ,
1314- false_p = False ,
1315- duplicate = False ,
1316- out_of_scope = False ,
1317- active = True ,
1318- mitigated__isnull = True ).count ()
1319- active_endpoints = Endpoint .objects .filter (
1320- product = self .product ,
1311+ self ._product = product
1312+ self ._title = title
1313+ self ._tab = tab
1314+ self ._engagement = None
1315+
1316+ @cached_property
1317+ def engagement_count (self ):
1318+ return Engagement .objects .filter (
1319+ product = self ._product , active = True ).count ()
1320+
1321+ @cached_property
1322+ def open_findings_count (self ):
1323+ return Finding .objects .filter (
1324+ test__engagement__product = self ._product ,
1325+ false_p = False ,
1326+ duplicate = False ,
1327+ out_of_scope = False ,
1328+ active = True ,
1329+ mitigated__isnull = True ).count ()
1330+
1331+ @cached_property
1332+ def _active_endpoints (self ):
1333+ return Endpoint .objects .filter (
1334+ product = self ._product ,
13211335 status_endpoint__mitigated = False ,
13221336 status_endpoint__false_positive = False ,
13231337 status_endpoint__out_of_scope = False ,
13241338 status_endpoint__risk_accepted = False ,
13251339 )
1326- self .endpoints_count = active_endpoints .distinct ().count ()
1327- self .endpoint_hosts_count = active_endpoints .values ("host" ).distinct ().count ()
1328- self .benchmark_type = Benchmark_Type .objects .filter (
1340+
1341+ @cached_property
1342+ def endpoints_count (self ):
1343+ return self ._active_endpoints .distinct ().count ()
1344+
1345+ @cached_property
1346+ def endpoint_hosts_count (self ):
1347+ return self ._active_endpoints .values ("host" ).distinct ().count ()
1348+
1349+ @cached_property
1350+ def benchmark_type (self ):
1351+ return Benchmark_Type .objects .filter (
13291352 enabled = True ).order_by ("name" )
1330- self .engagement = None
13311353
13321354 def setTab (self , tab ):
1333- self .tab = tab
1355+ self ._tab = tab
13341356
13351357 def setEngagement (self , engagement ):
1336- self .engagement = engagement
1358+ self ._engagement = engagement
13371359
1360+ @property
13381361 def engagement (self ):
1339- return self .engagement
1362+ return self ._engagement
13401363
1364+ @property
13411365 def tab (self ):
1342- return self .tab
1366+ return self ._tab
13431367
13441368 def setTitle (self , title ):
1345- self .title = title
1369+ self ._title = title
13461370
1371+ @property
13471372 def title (self ):
1348- return self .title
1373+ return self ._title
13491374
1375+ @property
13501376 def product (self ):
1351- return self .product
1377+ return self ._product
13521378
13531379 def engagements (self ):
13541380 return self .engagement_count
@@ -1362,9 +1388,6 @@ def endpoints(self):
13621388 def endpoint_hosts (self ):
13631389 return self .endpoint_hosts_count
13641390
1365- def benchmark_type (self ):
1366- return self .benchmark_type
1367-
13681391
13691392# Used to display the counts and enabled tabs in the product view
13701393def tab_view_count (product_id ):
0 commit comments