X-Git-Url: http://git.ithinksw.org/philo.git/blobdiff_plain/fa49fa4a08db28b3473574fc7a690d87316e9af6..7b3b752db47aaa16e136e245a6e3f350c9566690:/philo/contrib/shipherd/models.py?ds=sidebyside diff --git a/philo/contrib/shipherd/models.py b/philo/contrib/shipherd/models.py index c8579a2..95be501 100644 --- a/philo/contrib/shipherd/models.py +++ b/philo/contrib/shipherd/models.py @@ -1,6 +1,9 @@ #encoding: utf-8 from UserDict import DictMixin +from hashlib import sha1 +from django.contrib.sites.models import Site +from django.core.cache import cache from django.core.exceptions import ValidationError from django.core.urlresolvers import NoReverseMatch from django.core.validators import RegexValidator, MinValueValidator @@ -21,9 +24,15 @@ class NavigationMapper(object, DictMixin): """ def __init__(self, node): self.node = node + self._cache = {} def __getitem__(self, key): - return Navigation.objects.get_for_node(self.node, key) + if key not in self._cache: + try: + self._cache[key] = Navigation.objects.get_for_node(self.node, key) + except Navigation.DoesNotExist: + self._cache[key] = None + return self._cache[key] def navigation(self): @@ -39,41 +48,64 @@ class NavigationManager(models.Manager): use_for_related = True def get_for_node(self, node, key): + cache_key = self._get_cache_key(node, key) + cached = cache.get(cache_key) + + if cached is None: + opts = Node._mptt_meta + left = getattr(node, opts.left_attr) + right = getattr(node, opts.right_attr) + tree_id = getattr(node, opts.tree_id_attr) + kwargs = { + "node__%s__lte" % opts.left_attr: left, + "node__%s__gte" % opts.right_attr: right, + "node__%s" % opts.tree_id_attr: tree_id + } + navs = self.filter(key=key, **kwargs).select_related('node').order_by('-node__%s' % opts.level_attr) + nav = navs[0] + roots = nav.roots.all().select_related('target_node').order_by('order') + item_opts = NavigationItem._mptt_meta + by_pk = {} + tree_ids = [] + + site_root_node = Site.objects.get_current().root_node + + for root in roots: + by_pk[root.pk] = root + tree_ids.append(getattr(root, item_opts.tree_id_attr)) + root._cached_children = [] + if root.target_node: + root.target_node.get_path(root=site_root_node) + root.navigation = nav + + kwargs = { + '%s__in' % item_opts.tree_id_attr: tree_ids, + '%s__lt' % item_opts.level_attr: nav.depth, + '%s__gt' % item_opts.level_attr: 0 + } + items = NavigationItem.objects.filter(**kwargs).select_related('target_node').order_by('level', 'order') + for item in items: + by_pk[item.pk] = item + item._cached_children = [] + parent_pk = getattr(item, '%s_id' % item_opts.parent_attr) + item.parent = by_pk[parent_pk] + item.parent._cached_children.append(item) + if item.target_node: + item.target_node.get_path(root=site_root_node) + + cached = roots + cache.set(cache_key, cached) + + return cached + + def _get_cache_key(self, node, key): opts = Node._mptt_meta left = getattr(node, opts.left_attr) right = getattr(node, opts.right_attr) - tree = getattr(node, opts.tree_id_attr) - kwargs = { - "node__%s__lte" % opts.left_attr: left, - "node__%s__gte" % opts.right_attr: right, - "node__%s" % opts.tree_id_attr: tree_id - } - navs = self.filter(key=key, **kwargs).order_by('-node__%s' % opts.level_attr) - nav = navs[0] - roots = nav.roots.all() - item_opts = NavigationItem._mptt_meta - by_pk = {} - tree_ids = [] + tree_id = getattr(node, opts.tree_id_attr) + parent_id = getattr(node, "%s_id" % opts.parent_attr) - for root in roots: - by_pk[root.pk] = pk - tree_ids.append(getattr(root, item_opts.tree_id_attr)) - root._cached_children = [] - - kwargs = { - '%s__in' % item_opts.tree_id_attr: tree_ids, - '%s__lt' % item_opts.level_attr: nav.depth, - '%s__gt' % item_opts.level_attr: 0 - } - items = NavigationItem.objects.filter(**kwargs).order_by('level', 'order') - for item in items: - by_pk[item.pk] = item - item._cached_children = [] - parent_pk = getattr(item, '%s_id' % item_opts.parent_attr) - item.parent = by_pk[parent_pk] - item.parent._cached_children.append(item) - - return roots + return sha1(unicode(left) + unicode(right) + unicode(tree_id) + unicode(parent_id) + unicode(node.pk) + unicode(key)).hexdigest() class Navigation(Entity): @@ -142,13 +174,15 @@ class NavigationItem(TreeEntity, TargetURLModel): # the same as the request path, check whether the target node is an ancestor # of the requested node. If so, this is active unless the target node # is the same as the ``host node`` for this navigation structure. - try: - host_node = self.get_root().navigation.node - except AttributeError: - pass - else: - if self.target_node != host_node and self.target_node.is_ancestor_of(request.node): - return True + root = self + + # The common case will be cached items, whose parents are cached with them. + while root.parent is not None: + root = root.parent + + host_node_id = root.navigation.node_id + if self.target_node.pk != host_node_id and self.target_node.is_ancestor_of(request.node): + return True return False