Merge branch 'develop' into fast_containers
[philo.git] / philo / templatetags / containers.py
index 722f2d8..2c55034 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,39 @@ 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:
-                                       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):
        """
        """
-       {% 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 +102,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 +120,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)