diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index f84ceacae..f6fa4af7b 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -1078,7 +1078,7 @@ class CablePathSerializer(serializers.ModelSerializer): @swagger_serializer_method(serializer_or_field=serializers.ListField) def get_path(self, obj): ret = [] - for node in obj.get_path(): + for node in obj.path_objects(): serializer = get_serializer_for_model(node, prefix='Nested') context = {'request': self.context['request']} ret.append(serializer(node, context=context).data) diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 5baa767be..372bf7655 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -324,11 +324,55 @@ class CablePath(models.Model): super().save(*args, **kwargs) # Record a direct reference to this CablePath on its originating object(s) - origins = [path_node_to_object(n) for n in self.path[0]] - origin_model = origins[0]._meta.model - origin_ids = [o.id for o in origins] + origin_model = self.origins[0]._meta.model + origin_ids = [o.id for o in self.origins] origin_model.objects.filter(pk__in=origin_ids).update(_path=self.pk) + @property + def origin_type(self): + ct_id, _ = decompile_path_node(self.path[0][0]) + return ContentType.objects.get_for_id(ct_id) + + @property + def destination_type(self): + if not self.is_complete: + return None + ct_id, _ = decompile_path_node(self.path[-1][0]) + return ContentType.objects.get_for_id(ct_id) + + @property + def path_objects(self): + """ + Cache and return the complete path as lists of objects, derived from their annotation within the path. + """ + if not hasattr(self, '_path_objects'): + self._path_objects = self._get_path() + return self._path_objects + + @property + def origins(self): + """ + Return the list of originating objects (from cache, if available). + """ + if hasattr(self, '_path_objects'): + return self.path_objects[0] + return [ + path_node_to_object(node) for node in self.path[0] + ] + + @property + def destinations(self): + """ + Return the list of destination objects (from cache, if available), if the path is complete. + """ + if not self.is_complete: + return [] + if hasattr(self, '_path_objects'): + return self.path_objects[-1] + return [ + path_node_to_object(node) for node in self.path[-1] + ] + @property def segment_count(self): return int(len(self.path) / 3) @@ -474,9 +518,7 @@ class CablePath(models.Model): """ Retrace the path from the currently-defined originating termination(s) """ - _new = self.from_origin([ - path_node_to_object(node) for node in self.path[0] - ]) + _new = self.from_origin(self.origins) if _new: self.path = _new.path self.is_complete = _new.is_complete @@ -486,7 +528,7 @@ class CablePath(models.Model): else: self.delete() - def get_path(self): + def _get_path(self): """ Return the path as a list of prefetched objects. """ @@ -518,22 +560,6 @@ class CablePath(models.Model): return path - def get_destination(self): - if not self.is_complete: - return None - return [ - path_node_to_object(node) for node in self.path[-1] - ] - - @property - def last_nodes(self): - """ - Return either the destination or the last node within the path. - """ - return [ - path_node_to_object(node) for node in self.path[-1] - ] - def get_cable_ids(self): """ Return all Cable IDs within the path. diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 546f82a6b..9bc444203 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -200,7 +200,7 @@ class PathEndpoint(models.Model): if origin._path is None: break - path.extend(*origin._path.get_path()) + path.extend(origin._path.path_objects) while (len(path)) % 3: # Pad to ensure we have complete three-tuples (e.g. for paths that end at a non-connected FrontPort) # by inserting empty entries immediately prior to the path's destination node(s) @@ -231,7 +231,7 @@ class PathEndpoint(models.Model): Caching accessor for the attached CablePath's destination (if any) """ if not hasattr(self, '_connected_endpoints'): - self._connected_endpoints = self._path.get_destination() if self._path else [] + self._connected_endpoints = self._path.destinations if self._path else [] return self._connected_endpoints diff --git a/netbox/dcim/utils.py b/netbox/dcim/utils.py index 0ea5f4580..e773dacc0 100644 --- a/netbox/dcim/utils.py +++ b/netbox/dcim/utils.py @@ -64,5 +64,4 @@ def rebuild_paths(terminations): with transaction.atomic(): for cp in cable_paths: cp.delete() - origins = [path_node_to_object(node) for node in cp.path[0]] - create_cablepath(origins) + create_cablepath(cp.origins)