- return NavigationQuerySet(self.model, using=self._db)
-
- def closest_navigation(self, node):
- """
- Returns the set of Navigation objects for a given node's navigation. This
- will be the most recent set of defined hosted navigation among the node's
- ancestors. Lookups are cached so that subsequent lookups for the same node
- don't hit the database.
- """
- try:
- return self._get_cache_for(self.db, node)
- except KeyError:
- # Find the most recent host!
- ancestors = node.get_ancestors(ascending=True, include_self=True).annotate(num_navigation=models.Count("hosted_navigation"))
+ return NavigationCacheQuerySet(self.model, using=self._db)
+
+ def get_cache_for(self, node, update_targets=True):
+ created = False
+ if not self.has_cache_for(node):
+ self.create_cache_for(node)
+ created = True
+
+ if update_targets and not created:
+ self.update_targets_for(node)
+
+ return self.__class__._cache[self.db][node]
+
+ def has_cache_for(self, node):
+ return self.db in self.__class__._cache and node in self.__class__._cache[self.db]
+
+ def create_cache_for(self, node):
+ "This method loops through the nodes ancestors and caches all unique navigation keys."
+ ancestors = node.get_ancestors(ascending=True, include_self=True)
+
+ nodes_to_cache = []
+
+ for node in ancestors:
+ if self.has_cache_for(node):
+ cache = self.get_cache_for(node).copy()
+ break
+ else:
+ nodes_to_cache.insert(0, node)
+ else:
+ cache = {}
+
+ for node in nodes_to_cache:
+ cache = cache.copy()
+ cache.update(self._build_cache_for(node))
+ self.__class__._cache.setdefault(self.db, {})[node] = cache
+
+ def _build_cache_for(self, node):
+ cache = {}
+ tree_id_attr = NavigationItem._mptt_meta.tree_id_attr
+ level_attr = NavigationItem._mptt_meta.level_attr
+
+ for navigation in node.navigation_set.all():
+ tree_ids = navigation.roots.values_list(tree_id_attr)
+ items = list(NavigationItem.objects.filter(**{'%s__in' % tree_id_attr: tree_ids, '%s__lt' % level_attr: navigation.depth}).order_by('order', 'lft'))