Removed template container discovery in favor of (potentially) larger queries.
[philo.git] / philo / templatetags / containers.py
index 1e2ff80..c9ef2a0 100644 (file)
@@ -1,13 +1,28 @@
+"""
+The container template tags are automatically included as builtins if :mod:`philo` is an installed app.
+
+"""
+
 from django import template
 from django.conf import settings
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ObjectDoesNotExist
 from django import template
 from django.conf import settings
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ObjectDoesNotExist
+from django.db.models import Q
 from django.utils.safestring import SafeUnicode, mark_safe
 
 
 register = template.Library()
 
 
 from django.utils.safestring import SafeUnicode, mark_safe
 
 
 register = template.Library()
 
 
+CONTAINER_CONTEXT_KEY = 'philo_container_context'
+
+
+class ContainerContext(object):
+       def __init__(self, contentlets, references):
+               self.contentlets = dict(((c.name, c) for c in contentlets))
+               self.references = dict((((c.name, ContentType.objects.get_for_id(c.content_type_id)), c) for c in references))
+
+
 class ContainerNode(template.Node):
        def __init__(self, name, references=None, as_var=None):
                self.name = name
 class ContainerNode(template.Node):
        def __init__(self, name, references=None, as_var=None):
                self.name = name
@@ -15,54 +30,55 @@ class ContainerNode(template.Node):
                self.references = references
        
        def render(self, context):
                self.references = references
        
        def render(self, context):
-               content = settings.TEMPLATE_STRING_IF_INVALID
-               if 'page' in context:
-                       container_content = self.get_container_content(context)
-               else:
-                       container_content = None
+               container_content = self.get_container_content(context)
                
                if self.as_var:
                        context[self.as_var] = container_content
                        return ''
                
                
                if self.as_var:
                        context[self.as_var] = container_content
                        return ''
                
-               if not container_content:
-                       return ''
-               
                return container_content
        
        def get_container_content(self, context):
                return container_content
        
        def get_container_content(self, context):
-               page = context['page']
+               try:
+                       container_context = context.render_context[CONTAINER_CONTEXT_KEY]
+               except KeyError:
+                       try:
+                               page = context['page']
+                       except KeyError:
+                               return settings.TEMPLATE_STRING_IF_INVALID
+                       
+                       contentlets = page.contentlets.all()
+                       references = page.contentreferences.all()
+                       
+                       container_context = ContainerContext(contentlets, references)
+                       context.render_context[CONTAINER_CONTEXT_KEY] = container_context
+               
                if self.references:
                        # Then it's a content reference.
                        try:
                if self.references:
                        # Then it's a content reference.
                        try:
-                               contentreference = page.contentreferences.get(name__exact=self.name, content_type=self.references)
-                               content = contentreference.content
-                       except ObjectDoesNotExist:
+                               contentreference = container_context.references[(self.name, self.references)]
+                       except KeyError:
                                content = ''
                                content = ''
+                       else:
+                               content = contentreference.content
                else:
                        # Otherwise it's a contentlet.
                        try:
                else:
                        # Otherwise it's a contentlet.
                        try:
-                               contentlet = page.contentlets.get(name__exact=self.name)
-                               if '{%' in contentlet.content or '{{' in contentlet.content:
-                                       try:
-                                               content = template.Template(contentlet.content, name=contentlet.name).render(context)
-                                       except template.TemplateSyntaxError, error:
-                                               if settings.DEBUG:
-                                                       content = ('[Error parsing contentlet \'%s\': %s]' % (self.name, error))
-                                               else:
-                                                       content = settings.TEMPLATE_STRING_IF_INVALID
-                               else:
-                                       content = contentlet.content
-                       except ObjectDoesNotExist:
-                               content = settings.TEMPLATE_STRING_IF_INVALID
-                       content = mark_safe(content)
+                               contentlet = container_context.contentlets[self.name]
+                       except KeyError:
+                               content = ''
+                       else:
+                               content = contentlet.content
                return content
 
 
                return content
 
 
-def do_container(parser, token):
+@register.tag
+def container(parser, token):
        """
        """
+       If a template using this tag is used to render a :class:`.Page`, that :class:`.Page` will have associated content which can be set in the admin interface. If a content type is referenced, then a :class:`.ContentReference` object will be created; otherwise, a :class:`.Contentlet` object will be created.
+       
        Usage::
        Usage::
-               
+       
                {% container <name> [[references <app_label>.<model_name>] as <variable>] %}
        
        """
                {% container <name> [[references <app_label>.<model_name>] as <variable>] %}
        
        """
@@ -79,7 +95,7 @@ def do_container(parser, token):
                                if option_token == 'references':
                                        try:
                                                app_label, model = remaining_tokens.pop(0).strip('"').split('.')
                                if option_token == 'references':
                                        try:
                                                app_label, model = remaining_tokens.pop(0).strip('"').split('.')
-                                               references = ContentType.objects.get(app_label=app_label, model=model)
+                                               references = ContentType.objects.get_by_natural_key(app_label, model)
                                        except IndexError:
                                                raise template.TemplateSyntaxError('"%s" template tag option "references" requires an argument specifying a content type' % tag)
                                        except ValueError:
                                        except IndexError:
                                                raise template.TemplateSyntaxError('"%s" template tag option "references" requires an argument specifying a content type' % tag)
                                        except ValueError:
@@ -97,6 +113,3 @@ def do_container(parser, token):
                
        else: # error
                raise template.TemplateSyntaxError('"%s" template tag provided without arguments (at least one required)' % tag)
                
        else: # error
                raise template.TemplateSyntaxError('"%s" template tag provided without arguments (at least one required)' % tag)
-
-
-register.tag('container', do_container)