1
0
mirror of https://github.com/django/django.git synced 2025-07-06 02:39:12 +00:00

Merged to r778. Added template tag decorators:

* simple_tag : transforms a function outputting a string into a tag. 
                arguments are converted into tag arguments, and defaults are 
                supported. 
 * inclusion_tag : transforms a function outputting a dictionary into a tag 
                which renders an included template. The name of the template is
                given as an argument to the decorator, and the dictionary 
                returnd by the function is used to populate the context 
                to render the included template. The template is loaded at
                compile time, and the nodelist cached. 

Also made the {%include %} tag load the template at compile time if the argument is a constant string.  


git-svn-id: http://code.djangoproject.com/svn/django/branches/new-admin@780 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robert Wittams 2005-10-05 21:41:41 +00:00
parent 555502e208
commit e3f3d4e86f
5 changed files with 196 additions and 138 deletions

View File

@ -87,7 +87,7 @@
{% if auto_populated_fields %} {% if auto_populated_fields %}
<script type="text/javascript"> <script type="text/javascript">
{% auto_populated_field_script auto_populated_fields %} {% auto_populated_field_script auto_populated_fields change %}
</script> </script>
{% endif %} {% endif %}

View File

@ -110,7 +110,7 @@ IGNORABLE_404_ENDS = ('mail.pl', 'mailform.pl', 'mail.cgi', 'mailform.cgi', 'fav
SECRET_KEY = '' SECRET_KEY = ''
# Path to the "jing" executable -- needed to validate XMLFields # Path to the "jing" executable -- needed to validate XMLFields
JING_PATH = "/usr/bin/jng" JING_PATH = "/usr/bin/jing"
############## ##############
# MIDDLEWARE # # MIDDLEWARE #

View File

@ -227,6 +227,15 @@ class SsiNode(template.Node):
return '' # Fail silently for invalid included templates. return '' # Fail silently for invalid included templates.
return output return output
class ConstantIncludeNode(template.Node):
def __init__(self, template_path):
t = template_loader.get_template(template_path)
self.nodelist = t.nodelist
def render(self, context):
return self.nodelist.render(context)
class IncludeNode(template.Node): class IncludeNode(template.Node):
def __init__(self, template_path_var): def __init__(self, template_path_var):
self.template_path_var = template_path_var self.template_path_var = template_path_var
@ -631,6 +640,10 @@ def do_include(parser, token):
parsed = False parsed = False
if len(bits) != 2: if len(bits) != 2:
raise template.TemplateSyntaxError, "'include' tag takes one argument: the path to the template to be included" raise template.TemplateSyntaxError, "'include' tag takes one argument: the path to the template to be included"
path = bits[1]
if path[0] in ('"', "'") and path[-1] == path[0]:
return ConstantIncludeNode(path[1:-1])
return IncludeNode(bits[1]) return IncludeNode(bits[1])
def do_load(parser, token): def do_load(parser, token):
@ -648,8 +661,8 @@ def do_load(parser, token):
# check at compile time that the module can be imported # check at compile time that the module can be imported
try: try:
LoadNode.load_taglib(taglib) LoadNode.load_taglib(taglib)
except ImportError: except ImportError, e:
raise template.TemplateSyntaxError, "'%s' is not a valid tag library" % taglib raise template.TemplateSyntaxError, "'%s' is not a valid tag library, %s" % (taglib, e)
return LoadNode(taglib) return LoadNode(taglib)
def do_now(parser, token): def do_now(parser, token):

View File

@ -0,0 +1,70 @@
from inspect import getargspec
from django.core import template
from django.core.template_loader import render_to_string, get_template
from django.utils.functional import curry
def gen_compile_func(params, defaults, name, node_class, parser, token):
#look in tags for
bits = token.contents.split()[1:]
bmax = len(params)
def_len = defaults and len(defaults) or 0
bmin = bmax - def_len
if( len(bits) < bmin or len(bits) > bmax ):
if bmin == bmax:
message = "%s takes %s arguments" % (name, bmin)
else:
message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
raise template.TemplateSyntaxError(message)
return node_class(bits)
def simple_tag(func):
(params,_, _, defaults) = getargspec(func)
class TNode(template.Node):
def __init__(self, vars_to_resolve):
#get the vars to resolve
self.vars_to_resolve = vars_to_resolve
def render(self, context):
resolved_vars = [template.resolve_variable(var, context)
for var in self.vars_to_resolve]
return func(*resolved_vars)
compile_func = curry(gen_compile_func, params, defaults, func.__name__, TNode)
compile_func.__doc__ = func.__doc__
template.register_tag(func.__name__, compile_func)
return func
def inclusion_tag(file_name, context_class=template.Context, takes_context=False):
def dec(func):
(params,_, _, defaults) = getargspec(func)
if takes_context:
if params[0] == 'context':
params = params[1:]
else:
raise template.TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'" )
class TNode(template.Node):
def __init__(self, vars_to_resolve):
self.vars_to_resolve = vars_to_resolve
def render(self, context):
resolved_vars = [template.resolve_variable(var, context)
for var in self.vars_to_resolve]
if takes_context:
args = [context] + resolved_vars
else:
args = resolved_vars
dict = func(*args)
if not getattr(self, 'nodelist', False):
t = get_template(file_name)
self.nodelist = t.nodelist
return self.nodelist.render(context_class(dict))
compile_func = curry(gen_compile_func, params, defaults, func.__name__, TNode)
compile_func.__doc__ = func.__doc__
template.register_tag(func.__name__, compile_func)
return func
return dec

View File

@ -5,6 +5,8 @@ from django.utils.text import capfirst
from django.utils.html import escape from django.utils.html import escape
from django.utils.functional import curry from django.utils.functional import curry
from django.core.template_decorators import simple_tag, inclusion_tag
from django.views.admin.main import AdminBoundField from django.views.admin.main import AdminBoundField
import re import re
@ -13,21 +15,15 @@ word_re = re.compile('[A-Z][a-z]+')
def class_name_to_underscored(name): def class_name_to_underscored(name):
return '_'.join([ s.lower() for s in word_re.findall(name)[:-1] ]) return '_'.join([ s.lower() for s in word_re.findall(name)[:-1] ])
#@simple_tag
class IncludeAdminScriptNode(template.Node): def include_admin_script(script_path):
def __init__(self, var):
self.var = var
def render(self, context):
resolved = template.resolve_variable(self.var, context)
return '<script type="text/javascript" src="%s%s"></script>' % \ return '<script type="text/javascript" src="%s%s"></script>' % \
(ADMIN_MEDIA_PREFIX, resolved) (ADMIN_MEDIA_PREFIX, script_path)
include_admin_script = simple_tag(include_admin_script)
class SubmitRowNode(template.Node):
def __init__(self):
pass
def render(self, context): #@inclusion_tag('admin_submit_line', takes_context=True)
def submit_row(context):
change = context['change'] change = context['change']
add = context['add'] add = context['add']
show_delete = context['show_delete'] show_delete = context['show_delete']
@ -36,8 +32,7 @@ class SubmitRowNode(template.Node):
has_delete_permission = context['has_delete_permission'] has_delete_permission = context['has_delete_permission']
is_popup = context['is_popup'] is_popup = context['is_popup']
return {
output = render_to_string('admin_submit_line', {
'onclick_attrib' : (ordered_objects and change 'onclick_attrib' : (ordered_objects and change
and 'onclick="submitOrderForm();"' or ''), and 'onclick="submitOrderForm();"' or ''),
'show_delete_link' : (not is_popup and has_delete_permission 'show_delete_link' : (not is_popup and has_delete_permission
@ -46,50 +41,35 @@ class SubmitRowNode(template.Node):
'show_save_and_add_another': not is_popup and (not save_as or add), 'show_save_and_add_another': not is_popup and (not save_as or add),
'show_save_and_continue': not is_popup, 'show_save_and_continue': not is_popup,
'show_save': True 'show_save': True
}, context); }
context.pop()
return output;
class AdminFieldBoundNode(template.Node): srdec = inclusion_tag('admin_submit_line', takes_context=True)
def __init__(self, argument): submit_row = srdec(submit_row)
self.argument = argument
def render(self, context): #@simple_tag
argument_val = template.resolve_variable(self.argument, context) def field_label(bound_field):
if (isinstance(argument_val, list)): class_names = []
bound_fields = argument_val if isinstance(bound_field.field, meta.BooleanField):
else: class_names.append("vCheckboxLabel")
bound_fields = [argument_val] else:
add = context['add'] if not bound_field.field.blank:
change = context['change'] class_names.append('required')
if not bound_field.first:
class_names.append('inline')
context.push() class_str = class_names and ' class="%s"' % ' '.join(class_names) or ''
context['bound_fields'] = bound_fields return '<label for="%s"%s>%s:</label> ' % \
context['class_names'] = " ".join(self.get_class_names(bound_fields)) (bound_field.element_id, class_str,
t = template_loader.get_template("admin_field") capfirst(bound_field.field.verbose_name) )
output = t.render(context) field_label = simple_tag(field_label)
context.pop()
return output
def get_class_names(self, bound_fields):
class_names = ['form-row']
for bound_field in bound_fields:
for f in bound_field.form_fields:
if f.errors():
class_names.append('errors')
break
# Assumes BooleanFields won't be stacked next to each other!
if isinstance(bound_fields[0].field, meta.BooleanField):
class_names.append('checkbox-row')
return class_names
class FieldWidgetNode(template.Node): class FieldWidgetNode(template.Node):
def __init__(self, bound_field_var): def __init__(self, bound_field_var):
self.bound_field_var = bound_field_var self.bound_field_var = bound_field_var
self.nodelists = {}
t = template_loader.get_template("widget/default")
self.default = t.nodelist
def render(self, context): def render(self, context):
@ -100,22 +80,26 @@ class FieldWidgetNode(template.Node):
context.push() context.push()
context['bound_field'] = bound_field context['bound_field'] = bound_field
klass = bound_field.field.__class__ klass = bound_field.field.__class__
t = None if not self.nodelists.has_key(klass):
while klass: t = None
try: while klass:
field_class_name = klass.__name__ try:
template_name = "widget/%s" % \ field_class_name = klass.__name__
class_name_to_underscored(field_class_name) template_name = "widget/%s" % \
class_name_to_underscored(field_class_name)
t = template_loader.get_template(template_name)
break
except template.TemplateDoesNotExist:
klass = bool(klass.__bases__) and klass.__bases__[0] or None
t = template_loader.get_template(template_name) if t == None:
break nodelist = self.default
except template.TemplateDoesNotExist: else:
klass = (len(klass.__bases__) > 0) and klass.__bases__[0] or None nodelist = t.nodelist
if t == None: self.nodelists[klass] = nodelist
t = template_loader.get_template("widget/default")
output = t.render(context) output = self.nodelists[klass].render(context)
context.pop() context.pop()
return output return output
@ -178,70 +162,37 @@ class EditInlineNode(template.Node):
context['original_row_needed'] = max([fw.use_raw_id_admin() for fw in field_wrapper_list]) context['original_row_needed'] = max([fw.use_raw_id_admin() for fw in field_wrapper_list])
# context['name_prefix'] = "%s." % (var_name,) # context['name_prefix'] = "%s." % (var_name,)
class FieldLabelNode(template.Node):
def __init__(self, bound_field_var):
self.bound_field_var = bound_field_var
def render(self, context): #@simple_tag
bound_field = template.resolve_variable(self.bound_field_var, context) def output_all(form_fields):
class_names = [] return ''.join([str(f) for f in form_fields])
if isinstance(bound_field.field, meta.BooleanField): output_all = simple_tag(output_all)
class_names.append("vCheckboxLabel")
#@simple_tag
def auto_populated_field_script(auto_pop_fields, change = False):
for field in auto_pop_fields:
t = []
if change:
t.append('document.getElementById("id_%s")._changed = true;' % field.name )
else: else:
if not bound_field.field.blank: t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name)
class_names.append('required')
if not bound_field.first:
class_names.append('inline')
class_str = class_names and ' class="%s"' % ' '.join(class_names) or '' add_values = ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from])
return '<label for="%s"%s>%s:</label> ' % (bound_field.element_id, class_str, capfirst(bound_field.field.verbose_name) ) for f in field.prepopulate_from:
t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if(e._changed) { e.value = URLify(%s, %s);} } ' % (f, field.name, add_values, field.maxlength) )
class OutputAllNode(template.Node): return ''.join(t)
def __init__(self, form_fields_var): auto_populated_field_script = simple_tag(auto_populated_field_script)
self.form_fields_var = form_fields_var
def render(self, context):
form_fields = template.resolve_variable(self.form_fields_var, context)
return ''.join([str(f) for f in form_fields])
class AutoPopulatedFieldScriptNode(template.Node):
def __init__(self, auto_pop_var):
self.auto_pop_var = auto_pop_var
def render(self,context):
auto_pop_fields = template.resolve_variable(self.auto_pop_var, context)
change = context['change']
for field in auto_pop_fields:
t = []
if change:
t.append('document.getElementById("id_%s")._changed = true;' % field.name )
else:
t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name)
add_values = ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from])
for f in field.prepopulate_from:
t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if(e._changed) { e.value = URLify(%s, %s);} } ' % (f, field.name, add_values, field.maxlength) )
return ''.join(t)
class FilterInterfaceScriptMaybeNode(template.Node):
def __init__(self, bound_field_var):
self.bound_field_var = bound_field_var
def render(self, context):
bound_field = template.resolve_variable(self.bound_field_var, context)
f = bound_field.field
if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface:
return '<script type="text/javascript">addEvent(window, "load", function(e) { SelectFilter.init("id_%s", "%s", %s, %r); });</script>\n' % (f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)
else:
return ''
def do_submit_row(parser, token):
return SubmitRowNode()
#@simple_tag
def filter_interface_script_maybe(bound_field):
f = bound_field.field
if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface:
return '<script type="text/javascript">addEvent(window, "load", function(e) { SelectFilter.init("id_%s", "%s", %s, %r); });</script>\n' % (f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)
else:
return ''
filter_interface_script_maybe = simple_tag(filter_interface_script_maybe)
def do_one_arg_tag(node_factory, parser,token): def do_one_arg_tag(node_factory, parser,token):
tokens = token.contents.split() tokens = token.contents.split()
@ -251,14 +202,8 @@ def do_one_arg_tag(node_factory, parser,token):
one_arg_tag_nodes = [ one_arg_tag_nodes = [
IncludeAdminScriptNode,
AdminFieldBoundNode,
FieldLabelNode,
FieldWidgetNode, FieldWidgetNode,
OutputAllNode,
EditInlineNode, EditInlineNode,
AutoPopulatedFieldScriptNode,
FilterInterfaceScriptMaybeNode,
] ]
@ -267,9 +212,39 @@ def register_one_arg_tag(node):
parse_func = curry(do_one_arg_tag, node) parse_func = curry(do_one_arg_tag, node)
template.register_tag(tag_name, parse_func) template.register_tag(tag_name, parse_func)
for node in one_arg_tag_nodes: for node in one_arg_tag_nodes:
register_one_arg_tag(node) register_one_arg_tag(node)
template.register_tag('submit_row', do_submit_row )
#@inclusion_tag('admin_field', takes_context=True)
def admin_field_bound(context, argument_val):
if (isinstance(argument_val, list)):
bound_fields = argument_val
else:
bound_fields = [argument_val]
add = context['add']
change = context['change']
class_names = ['form-row']
for bound_field in bound_fields:
for f in bound_field.form_fields:
if f.errors():
class_names.append('errors')
break
# Assumes BooleanFields won't be stacked next to each other!
if isinstance(bound_fields[0].field, meta.BooleanField):
class_names.append('checkbox-row')
return {
'add' : context['add'],
'change' : context['change'],
'bound_fields' : bound_fields,
'class_names' : " ".join(class_names)
}
afbdec = inclusion_tag('admin_field', takes_context=True)
admin_field_bound = afbdec(admin_field_bound)