Source code for shuup.utils.mptt

# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals


[docs]def get_cached_trees(queryset): """ Takes a list/queryset of model objects in MPTT left (depth-first) order and caches the children and parent on each node. This allows up and down traversal through the tree without the need for further queries. Use cases include using a recursively included template or arbitrarily traversing trees. NOTE: nodes _must_ be passed in the correct (depth-first) order. If they aren't, a ValueError will be raised. Returns a list of top-level nodes. If a single tree was provided in its entirety, the list will of course consist of just the tree's root node. For filtered querysets, if no ancestors for a node are included in the queryset, it will appear in the returned list as a top-level node. Aliases to this function are also available: ``mptt.templatetags.mptt_tag.cache_tree_children`` Use for recursive rendering in templates. ``mptt.querysets.TreeQuerySet.get_cached_trees`` Useful for chaining with queries; e.g., `Node.objects.filter(**kwargs).get_cached_trees()` FIXME: This method fixed the original `mptt.utils.get_cached_trees` method as it doesn't consider filtered querysets that might not contain all tree nodes. """ current_path = [] top_nodes = [] if queryset: # Get the model's parent-attribute name parent_attr = queryset[0]._mptt_meta.parent_attr root_level = None is_filtered = hasattr(queryset, "query") and queryset.query.has_filters() for obj in queryset: # Get the current mptt node level node_level = obj.get_level() if root_level is None or (is_filtered and node_level < root_level): # First iteration, so set the root level to the top node level root_level = node_level elif node_level < root_level: # ``queryset`` was a list or other iterable (unable to order), # and was provided in an order other than depth-first raise ValueError("Error! Node %s not in depth-first order." % (type(queryset),)) # Set up the attribute on the node that will store cached children, # which is used by ``MPTTModel.get_children`` obj._cached_children = [] # Remove nodes not in the current branch while len(current_path) > node_level - root_level: current_path.pop(-1) if node_level == root_level: # Add the root to the list of top nodes, which will be returned top_nodes.append(obj) else: # Cache the parent on the current node, and attach the current # node to the parent's list of children _parent = current_path[-1] setattr(obj, parent_attr, _parent) _parent._cached_children.append(obj) if root_level == 0: # get_ancestors() can use .parent.parent.parent... setattr(obj, "_mptt_use_cached_ancestors", True) # Add the current node to end of the current path - the last node # in the current path is the parent for the next iteration, unless # the next iteration is higher up the tree (a new branch), in which # case the paths below it (e.g., this one) will be removed from the # current path during the next iteration current_path.append(obj) return top_nodes