Clarified error messages and docstrings in models/nodes.py. Removed an extraneous...
[philo.git] / models / nodes.py
index 58a5b99..526400c 100644 (file)
@@ -10,16 +10,13 @@ from django.template import add_to_builtins as register_templatetags
 from inspect import getargspec
 from philo.exceptions import MIDDLEWARE_NOT_CONFIGURED
 from philo.models.base import TreeEntity, Entity, QuerySetMapper, register_value_model
-from philo.models.fields import JSONField
 from philo.utils import ContentTypeSubclassLimiter
 from philo.validators import RedirectValidator
 from philo.exceptions import ViewCanNotProvideSubpath, ViewDoesNotProvideSubpaths, AncestorDoesNotExist
 from philo.signals import view_about_to_render, view_finished_rendering
-from mptt.templatetags.mptt_tags import cache_tree_children
 
 
 _view_content_type_limiter = ContentTypeSubclassLimiter(None)
-DEFAULT_NAVIGATION_DEPTH = 3
 
 
 class Node(TreeEntity):
@@ -51,43 +48,6 @@ class Node(TreeEntity):
                except AncestorDoesNotExist, ViewDoesNotExist:
                        return None
        
-       def get_navigation(self, depth=DEFAULT_NAVIGATION_DEPTH):
-               max_depth = depth + self.get_level()
-               tree = cache_tree_children(self.get_descendants(include_self=True).filter(level__lte=max_depth))
-               
-               def get_nav(parent, nodes):
-                       node_overrides = dict([(override.child.pk, override) for override in NodeNavigationOverride.objects.filter(parent=parent, child__in=nodes).select_related('child')])
-                       
-                       navigation_list = []
-                       
-                       for node in nodes:
-                               node._override = node_overrides.get(node.pk, None)
-                               
-                               if node._override:
-                                       if node._override.hide:
-                                               continue
-                                       navigation = node._override.get_navigation(node, max_depth)
-                               else:
-                                       navigation = node.view.get_navigation(node, max_depth)
-                               
-                               if not node.is_leaf_node() and node.get_level() < max_depth:
-                                       children = navigation.get('children', [])
-                                       children += get_nav(node, node.get_children())
-                                       navigation['children'] = children
-                               
-                               if 'children' in navigation:
-                                       navigation['children'].sort(cmp=lambda x,y: cmp(x['order'], y['order']))
-                               
-                               navigation_list.append(navigation)
-                       
-                       return navigation_list
-               
-               return get_nav(self.parent, tree)
-       
-       def save(self):
-               super(Node, self).save()
-               
-       
        class Meta:
                app_label = 'philo'
 
@@ -96,39 +56,6 @@ class Node(TreeEntity):
 models.ForeignKey(Node, related_name='sites', null=True, blank=True).contribute_to_class(Site, 'root_node')
 
 
-class NodeNavigationOverride(Entity):
-       parent = models.ForeignKey(Node, related_name="child_navigation_overrides", blank=True, null=True)
-       child = models.ForeignKey(Node, related_name="navigation_overrides")
-       
-       title = models.CharField(max_length=100, blank=True)
-       url = models.CharField(max_length=200, validators=[RedirectValidator()], blank=True)
-       order = models.PositiveSmallIntegerField(blank=True, null=True)
-       child_navigation = JSONField()
-       hide = models.BooleanField()
-       
-       def get_navigation(self, node, depth, current_depth):
-               if self.child_navigation:
-                       depth = current_depth
-               default = node.view.get_navigation(depth, current_depth)
-               if self.url:
-                       default['url'] = self.url
-               if self.title:
-                       default['title'] = self.title
-               if self.order:
-                       default['order'] = self.order
-               if isinstance(self.child_navigation, list):
-                       if 'children' in default:
-                               default['children'] += self.child_navigation
-                       else:
-                               default['children'] = self.child_navigation
-               return default
-       
-       class Meta:
-               ordering = ['order']
-               unique_together = ('parent', 'child',)
-               app_label = 'philo'
-
-
 class View(Entity):
        nodes = generic.GenericRelation(Node, content_type_field='view_content_type', object_id_field='view_object_id')
        
@@ -162,23 +89,7 @@ class View(Entity):
                return response
        
        def actually_render_to_response(self, request, extra_context=None):
-               raise NotImplementedError('View subclasses must implement render_to_response.')
-       
-       def get_navigation(self, node, max_depth):
-               """
-               Subclasses should implement get_navigation to support auto-generated navigation.
-               max_depth is the deepest `level` that should be generated; node is the node that
-               is asking for the navigation. This method should return a dictionary of the form:
-                       {
-                               'url': url,
-                               'title': title,
-                               'order': order, # None for no ordering.
-                               'children': [ # Optional
-                                       <similar child navigation dictionaries>
-                               ]
-                       }
-               """
-               raise NotImplementedError('View subclasses must implement get_navigation.')
+               raise NotImplementedError('View subclasses must implement actually_render_to_response.')
        
        class Meta:
                abstract = True
@@ -191,7 +102,7 @@ class MultiView(View):
        accepts_subpath = True
        
        @property
-       def urlpatterns(self, obj):
+       def urlpatterns(self):
                raise NotImplementedError("MultiView subclasses must implement urlpatterns.")
        
        def actually_render_to_response(self, request, extra_context=None):
@@ -208,6 +119,39 @@ class MultiView(View):
                        kwargs['extra_context'] = extra_context
                return view(request, *args, **kwargs)
        
+       def reverse(self, view_name, args=None, kwargs=None, node=None):
+               """Shortcut method to handle the common pattern of getting the absolute url for a multiview's
+               subpaths."""
+               subpath = reverse(view_name, urlconf=self, args=args or [], kwargs=kwargs or {})
+               if node is not None:
+                       return '/%s/%s/' % (node.get_absolute_url().strip('/'), subpath.strip('/'))
+               return subpath
+       
+       def get_context(self):
+               """Hook for providing instance-specific context - such as the value of a Field - to all views."""
+               return {}
+       
+       def basic_view(self, field_name):
+               """
+               Given the name of a field on ``self``, accesses the value of
+               that field and treats it as a ``View`` instance. Creates a
+               basic context based on self.get_context() and any extra_context
+               that was passed in, then calls the ``View`` instance's
+               render_to_response() method. This method is meant to be called
+               to return a view function appropriate for urlpatterns.
+               """
+               field = self._meta.get_field(field_name)
+               view = getattr(self, field.name, None)
+               
+               def inner(request, extra_context=None, **kwargs):
+                       if not view:
+                               raise Http404
+                       context = self.get_context()
+                       context.update(extra_context or {})
+                       return view.render_to_response(request, extra_context=context)
+               
+               return inner
+       
        class Meta:
                abstract = True
 
@@ -229,7 +173,6 @@ class Redirect(View):
                app_label = 'philo'
 
 
-# Why does this exist?
 class File(View):
        """ For storing arbitrary files """