1 from django import template
2 from django.contrib.contenttypes.models import ContentType
3 from django.conf import settings
4 from philo.utils import LOADED_TEMPLATE_ATTR
7 register = template.Library()
10 class ConstantEmbedNode(template.Node):
11 """Analogous to the ConstantIncludeNode, this node precompiles several variables necessary for correct rendering - namely the referenced instance or the included template."""
12 def __init__(self, content_type, varname, object_pk=None, template_name=None, kwargs=None):
13 assert template_name is not None or object_pk is not None
14 self.content_type = content_type
15 self.varname = varname
18 for k, v in kwargs.items():
19 kwargs[k] = template.Variable(v)
22 if object_pk is not None:
23 self.compile_instance(object_pk)
27 if template_name is not None:
28 self.compile_template(template_name[1:-1])
32 def compile_instance(self, object_pk):
33 self.object_pk = object_pk
34 model = self.content_type.model_class()
36 self.instance = model.objects.get(pk=object_pk)
37 except model.DoesNotExist:
38 if not hasattr(self, 'object_pk') and settings.TEMPLATE_DEBUG:
39 # Then it's a constant node.
43 def compile_template(self, template_name):
45 self.template = template.loader.get_template(template_name)
46 except template.TemplateDoesNotExist:
47 if not hasattr(self, 'template_name') and settings.TEMPLATE_DEBUG:
48 # Then it's a constant node.
52 def render(self, context):
53 if self.template is not None:
54 if self.template is False:
55 return settings.TEMPLATE_STRING_IF_INVALID
57 if self.varname not in context:
58 context[self.varname] = {}
59 context[self.varname][self.content_type] = self.template
63 # Otherwise self.instance should be set. Render the instance with the appropriate template!
64 if self.instance is None or self.instance is False:
65 return settings.TEMPLATE_STRING_IF_INVALID
67 return self.render_template(context, self.instance)
69 def render_template(self, context, instance):
71 t = context[self.varname][self.content_type]
73 return settings.TEMPLATE_STRING_IF_INVALID
76 context['embedded'] = instance
78 for k, v in self.kwargs.items():
79 kwargs[k] = v.resolve(context)
80 context.update(kwargs)
81 t_rendered = t.render(context)
86 class EmbedNode(ConstantEmbedNode):
87 def __init__(self, content_type, varname, object_pk=None, template_name=None, kwargs=None):
88 assert template_name is not None or object_pk is not None
89 self.content_type = content_type
90 self.varname = varname
93 for k, v in kwargs.items():
94 kwargs[k] = template.Variable(v)
97 if object_pk is not None:
98 self.object_pk = template.Variable(object_pk)
100 self.object_pk = None
103 if template_name is not None:
104 self.template_name = template.Variable(template_name)
106 self.template_name = None
109 def render(self, context):
110 if self.template_name is not None:
111 template_name = self.template_name.resolve(context)
112 self.compile_template(template_name)
114 if self.object_pk is not None:
115 object_pk = self.object_pk.resolve(context)
116 self.compile_instance(object_pk)
118 return super(EmbedNode, self).render(context)
121 def get_embedded(self):
125 setattr(ConstantEmbedNode, LOADED_TEMPLATE_ATTR, property(get_embedded))
128 def do_embed(parser, token):
130 The {% embed %} tag can be used in three ways:
131 {% embed as <varname> %} :: This sets which variable will be used to track embedding template names for the current context. Default: "embed"
132 {% embed <app_label>.<model_name> with <template> %} :: Sets which template will be used to render a particular model.
133 {% embed <app_label>.<model_name> <object_pk> [<argname>=<value> ...]%} :: Embeds the instance specified by the given parameters in the document with the previously-specified template. Any kwargs provided will be passed into the context of the template.
135 args = token.split_contents()
139 raise template.TemplateSyntaxError('"%s" template tag must have at least three arguments.' % tag)
140 elif len(args) == 3 and args[1] == "as":
141 parser._embedNodeVarName = args[2]
142 return template.defaulttags.CommentNode()
144 if '.' not in args[1]:
145 raise template.TemplateSyntaxError('"%s" template tag expects the first argument to be of the form app_label.model' % tag)
147 app_label, model = args[1].split('.')
149 ct = ContentType.objects.get(app_label=app_label, model=model)
150 except ContentType.DoesNotExist:
151 raise template.TemplateSyntaxError('"%s" template tag option "references" requires an argument of the form app_label.model which refers to an installed content type (see django.contrib.contenttypes)' % tag)
153 varname = getattr(parser, '_embedNodeVarName', 'embed')
155 if args[2] == "with":
157 raise template.TemplateSyntaxError('"%s" template tag may have no more than four arguments.' % tag)
159 if args[3][0] in ['"', "'"] and args[3][0] == args[3][-1]:
160 return ConstantEmbedNode(ct, template_name=args[3], varname=varname)
162 return EmbedNode(ct, template_name=args[3], varname=varname)
165 remaining_args = args[3:]
167 for arg in remaining_args:
169 raise template.TemplateSyntaxError("Invalid keyword argument for '%s' template tag: %s" % (tag, arg))
170 k, v = arg.split('=')
173 return EmbedNode(ct, object_pk=object_pk, varname=varname, kwargs=kwargs)
176 register.tag('embed', do_embed)