mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Add CablePath.get_path() to prefetch path nodes
This commit is contained in:
@ -1,3 +1,5 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
@ -9,7 +11,7 @@ from taggit.managers import TaggableManager
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.fields import PathField
|
from dcim.fields import PathField
|
||||||
from dcim.utils import decompile_path_node, path_node_to_object
|
from dcim.utils import decompile_path_node
|
||||||
from extras.models import ChangeLoggedModel, CustomFieldModel, TaggedItem
|
from extras.models import ChangeLoggedModel, CustomFieldModel, TaggedItem
|
||||||
from extras.utils import extras_features
|
from extras.utils import extras_features
|
||||||
from utilities.fields import ColorField
|
from utilities.fields import ColorField
|
||||||
@ -366,8 +368,7 @@ class CablePath(models.Model):
|
|||||||
unique_together = ('origin_type', 'origin_id')
|
unique_together = ('origin_type', 'origin_id')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
path = ', '.join([str(path_node_to_object(node)) for node in self.path])
|
return f"Path #{self.pk}: {self.origin} to {self.destination} ({len(self.path)} nodes)"
|
||||||
return f"Path #{self.pk}: {self.origin} to {self.destination} via ({path})"
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
@ -376,6 +377,35 @@ class CablePath(models.Model):
|
|||||||
model = self.origin._meta.model
|
model = self.origin._meta.model
|
||||||
model.objects.filter(pk=self.origin.pk).update(_path=self.pk)
|
model.objects.filter(pk=self.origin.pk).update(_path=self.pk)
|
||||||
|
|
||||||
|
def get_path(self):
|
||||||
|
"""
|
||||||
|
Return the path as a list of prefetched objects.
|
||||||
|
"""
|
||||||
|
# Compile a list of IDs to prefetch for each type of model in the path
|
||||||
|
to_prefetch = defaultdict(list)
|
||||||
|
for node in self.path:
|
||||||
|
ct_id, object_id = decompile_path_node(node)
|
||||||
|
to_prefetch[ct_id].append(object_id)
|
||||||
|
|
||||||
|
# Prefetch path objects using one query per model type. Prefetch related devices where appropriate.
|
||||||
|
prefetched = {}
|
||||||
|
for ct_id, object_ids in to_prefetch.items():
|
||||||
|
model_class = ContentType.objects.get_for_id(ct_id).model_class()
|
||||||
|
queryset = model_class.objects.filter(pk__in=object_ids)
|
||||||
|
if hasattr(model_class, 'device'):
|
||||||
|
queryset = queryset.prefetch_related('device')
|
||||||
|
prefetched[ct_id] = {
|
||||||
|
obj.id: obj for obj in queryset
|
||||||
|
}
|
||||||
|
|
||||||
|
# Replicate the path using the prefetched objects.
|
||||||
|
path = []
|
||||||
|
for node in self.path:
|
||||||
|
ct_id, object_id = decompile_path_node(node)
|
||||||
|
path.append(prefetched[ct_id][object_id])
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
def get_total_length(self):
|
def get_total_length(self):
|
||||||
"""
|
"""
|
||||||
Return the sum of the length of each cable in the path.
|
Return the sum of the length of each cable in the path.
|
||||||
|
@ -11,7 +11,6 @@ from taggit.managers import TaggableManager
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.fields import MACAddressField
|
from dcim.fields import MACAddressField
|
||||||
from dcim.utils import path_node_to_object
|
|
||||||
from extras.models import ObjectChange, TaggedItem
|
from extras.models import ObjectChange, TaggedItem
|
||||||
from extras.utils import extras_features
|
from extras.utils import extras_features
|
||||||
from utilities.fields import NaturalOrderingField
|
from utilities.fields import NaturalOrderingField
|
||||||
@ -172,7 +171,7 @@ class PathEndpoint(models.Model):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
# Construct the complete path
|
# Construct the complete path
|
||||||
path = [self, *[path_node_to_object(obj) for obj in self._path.path]]
|
path = [self, *self._path.get_path()]
|
||||||
while (len(path) + 1) % 3:
|
while (len(path) + 1) % 3:
|
||||||
# Pad to ensure we have complete three-tuples (e.g. for paths that end at a RearPort)
|
# Pad to ensure we have complete three-tuples (e.g. for paths that end at a RearPort)
|
||||||
path.append(None)
|
path.append(None)
|
||||||
|
@ -21,15 +21,6 @@ def object_to_path_node(obj):
|
|||||||
return compile_path_node(ct.pk, obj.pk)
|
return compile_path_node(ct.pk, obj.pk)
|
||||||
|
|
||||||
|
|
||||||
def path_node_to_object(repr):
|
|
||||||
"""
|
|
||||||
Given a path node representation, return the corresponding object.
|
|
||||||
"""
|
|
||||||
ct_id, object_id = decompile_path_node(repr)
|
|
||||||
model_class = ContentType.objects.get(pk=ct_id).model_class()
|
|
||||||
return model_class.objects.get(pk=int(object_id))
|
|
||||||
|
|
||||||
|
|
||||||
def trace_path(node):
|
def trace_path(node):
|
||||||
from .models import FrontPort, RearPort
|
from .models import FrontPort, RearPort
|
||||||
|
|
||||||
|
@ -2049,7 +2049,7 @@ class PathTraceView(ObjectView):
|
|||||||
|
|
||||||
return render(request, 'dcim/cable_trace.html', {
|
return render(request, 'dcim/cable_trace.html', {
|
||||||
'obj': obj,
|
'obj': obj,
|
||||||
'path': path,
|
'path': obj.trace(),
|
||||||
'related_paths': related_paths,
|
'related_paths': related_paths,
|
||||||
'total_length': path.get_total_length(),
|
'total_length': path.get_total_length(),
|
||||||
})
|
})
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-5 col-sm-12 text-center">
|
<div class="col-md-5 col-sm-12 text-center">
|
||||||
{% for near_end, cable, far_end in path.origin.trace %}
|
{% for near_end, cable, far_end in path %}
|
||||||
|
|
||||||
{# Near end #}
|
{# Near end #}
|
||||||
{% if near_end.device %}
|
{% if near_end.device %}
|
||||||
@ -50,6 +50,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h3 class="text-success text-center">Trace completed!</h3>
|
<h3 class="text-success text-center">Trace completed!</h3>
|
||||||
|
<h5>Total segments: {{ path|length }}</h5>
|
||||||
{% if total_length %}
|
{% if total_length %}
|
||||||
<h5>Total length: {{ total_length|floatformat:"-2" }} Meters<h5>
|
<h5>Total length: {{ total_length|floatformat:"-2" }} Meters<h5>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
Reference in New Issue
Block a user