From 91801c950d2962cba8d831c63b698596d619dba9 Mon Sep 17 00:00:00 2001 From: Joseph Spiros Date: Mon, 3 May 2010 04:40:12 -0400 Subject: [PATCH] Added support for Node subclasses whose instances accept subpaths, and created a MultiNode abstract class for easy development of such classes using standard Django url resolution patterns. --- models.py | 44 +++++++++++++++++++++++++++++++++++++++----- views.py | 7 +++++-- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/models.py b/models.py index 4eb50f7..6691aca 100644 --- a/models.py +++ b/models.py @@ -19,7 +19,7 @@ from UserDict import DictMixin from templatetags.containers import ContainerNode from django.template.loader_tags import ExtendsNode, ConstantIncludeNode, IncludeNode from django.template.loader import get_template -from django.http import HttpResponse, HttpResponseServerError, HttpResponseRedirect +from django.http import Http404, HttpResponse, HttpResponseServerError, HttpResponseRedirect from django.core.servers.basehttp import FileWrapper @@ -162,18 +162,33 @@ class TreeManager(models.Manager): def roots(self): return self.filter(parent__isnull=True) - def get_with_path(self, path, root=None, pathsep='/'): + def get_with_path(self, path, root=None, absolute_result=True, pathsep='/'): + """ + Returns the object with the path, or None if there is no object with that path, + unless absolute_result is set to False, in which case it returns a tuple containing + the deepest object found along the path, and the remainder of the path after that + object as a string (or None in the case that there is no remaining path). + """ slugs = path.split(pathsep) obj = root + remaining_slugs = list(slugs) + remainder = None for slug in slugs: + remaining_slugs.remove(slug) if slug: # ignore blank slugs, handles for multiple consecutive pathseps try: obj = self.get(slug__exact=slug, parent__exact=obj) except self.model.DoesNotExist: - obj = None + if absolute_result: + obj = None + remaining_slugs.insert(0, slug) + remainder = pathsep.join(remaining_slugs) break if obj: - return obj + if absolute_result: + return obj + else: + return (obj, remainder) raise self.model.DoesNotExist('%s matching query does not exist.' % self.model._meta.object_name) @@ -233,13 +248,32 @@ class Node(TreeEntity): return HttpResponseServerError() +class MultiNode(Node): + accepts_subpath = True + + @property + def urlpatterns(self): + return [] + + def render_to_response(self, request, path=None, subpath=None): + if not subpath: + subpath = "" + subpath = "/" + subpath + from django.core.urlresolvers import resolve + view, args, kwargs = resolve(subpath, urlconf=self) + return view(request, *args, **kwargs) + + class Meta: + abstract = True + + class Redirect(Node): STATUS_CODES = ( (302, 'Temporary'), (301, 'Permanent'), ) target = models.URLField() - status_code = models.IntegerField(choices=STATUS_CODES, default=302, verbose_name="redirect type") + status_code = models.IntegerField(choices=STATUS_CODES, default=302, verbose_name='redirect type') def render_to_response(self, request, path=None, subpath=None): response = HttpResponseRedirect(self.target) diff --git a/views.py b/views.py index 5e4b7dd..c03f687 100644 --- a/views.py +++ b/views.py @@ -6,14 +6,17 @@ from models import Node def node_view(request, path=None, **kwargs): node = None + subpath = None if path is None: path = '/' try: current_site = Site.objects.get_current() if current_site: - node = Node.objects.get_with_path(path, root=current_site.root_node) + node, subpath = Node.objects.get_with_path(path, root=current_site.root_node, absolute_result=False) except Node.DoesNotExist: raise Http404 if not node: raise Http404 - return node.instance.render_to_response(request, path=path) + if subpath and not node.instance.accepts_subpath: + raise Http404 + return node.instance.render_to_response(request, path=path, subpath=subpath) -- 2.20.1