A fair amount of effort is wasted getting container content. Initial commit of a...
[philo.git] / philo / templatetags / containers.py
index 722f2d8..3370c78 100644 (file)
@@ -1,29 +1,36 @@
 """
 The container template tags are automatically included as builtins if :mod:`philo` is an installed app.
 
 """
 The container template tags are automatically included as builtins if :mod:`philo` is an installed app.
 
-.. templatetag:: container
-
-container
----------
-
-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::
-
-       {% container <name> [[references <app_label>.<model_name>] as <variable>] %}
-
 """
 
 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, page):
+               contentlet_specs, contentreference_specs = page.template.containers
+               
+               contentlets = page.contentlets.filter(name__in=contentlet_specs)
+               self.contentlets = dict(((c.name, c) for c in contentlets))
+               
+               q = Q()
+               for name, ct in contentreference_specs.items():
+                       q |= Q(name=name, content_type=ct)
+               references = page.contentreferences.filter(q)
+               self.references = dict(((c.name, 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
@@ -47,37 +54,49 @@ class ContainerNode(template.Node):
                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:
+                       container_context = ContainerContext(context['page'])
+                       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:
+                               contentlet = container_context.contentlets[self.name]
+                       except KeyError:
+                               content = ''
+                       else:
+                               content = contentlet.content
+                               
+                               if '{%' in content or '{{' in content:
                                        try:
                                                content = template.Template(contentlet.content, name=contentlet.name).render(context)
                                        try:
                                                content = template.Template(contentlet.content, name=contentlet.name).render(context)
-                                       except template.TemplateSyntaxError, error:
+                                       except template.TemplateSyntaxError, e:
                                                if settings.DEBUG:
                                                if settings.DEBUG:
-                                                       content = ('[Error parsing contentlet \'%s\': %s]' % (self.name, error))
+                                                       content = ('[Error parsing contentlet \'%s\': %s]' % (self.name, e))
                                                else:
                                                        content = settings.TEMPLATE_STRING_IF_INVALID
                                                else:
                                                        content = settings.TEMPLATE_STRING_IF_INVALID
-                               else:
-                                       content = contentlet.content
-                       except ObjectDoesNotExist:
-                               content = settings.TEMPLATE_STRING_IF_INVALID
                        content = mark_safe(content)
                return content
 
 
                        content = mark_safe(content)
                return content
 
 
-def do_container(parser, token):
+@register.tag
+def container(parser, token):
        """
        """
-       {% container <name> [[references <app_label>.<model_name>] as <variable>] %}
+       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::
+       
+               {% container <name> [[references <app_label>.<model_name>] as <variable>] %}
        
        """
        params = token.split_contents()
        
        """
        params = token.split_contents()
@@ -93,7 +112,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:
@@ -111,6 +130,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)