1
0
mirror of https://github.com/django/django.git synced 2025-07-05 02:09:13 +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 %}
<script type="text/javascript">
{% auto_populated_field_script auto_populated_fields %}
{% auto_populated_field_script auto_populated_fields change %}
</script>
{% endif %}

View File

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

View File

@ -227,6 +227,15 @@ class SsiNode(template.Node):
return '' # Fail silently for invalid included templates.
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):
def __init__(self, template_path_var):
self.template_path_var = template_path_var
@ -631,6 +640,10 @@ def do_include(parser, token):
parsed = False
if len(bits) != 2:
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])
def do_load(parser, token):
@ -648,8 +661,8 @@ def do_load(parser, token):
# check at compile time that the module can be imported
try:
LoadNode.load_taglib(taglib)
except ImportError:
raise template.TemplateSyntaxError, "'%s' is not a valid tag library" % taglib
except ImportError, e:
raise template.TemplateSyntaxError, "'%s' is not a valid tag library, %s" % (taglib, e)
return LoadNode(taglib)
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.functional import curry
from django.core.template_decorators import simple_tag, inclusion_tag
from django.views.admin.main import AdminBoundField
import re
@ -13,21 +15,15 @@ word_re = re.compile('[A-Z][a-z]+')
def class_name_to_underscored(name):
return '_'.join([ s.lower() for s in word_re.findall(name)[:-1] ])
class IncludeAdminScriptNode(template.Node):
def __init__(self, var):
self.var = var
def render(self, context):
resolved = template.resolve_variable(self.var, context)
#@simple_tag
def include_admin_script(script_path):
return '<script type="text/javascript" src="%s%s"></script>' % \
(ADMIN_MEDIA_PREFIX, resolved)
class SubmitRowNode(template.Node):
def __init__(self):
pass
(ADMIN_MEDIA_PREFIX, script_path)
include_admin_script = simple_tag(include_admin_script)
def render(self, context):
#@inclusion_tag('admin_submit_line', takes_context=True)
def submit_row(context):
change = context['change']
add = context['add']
show_delete = context['show_delete']
@ -36,8 +32,7 @@ class SubmitRowNode(template.Node):
has_delete_permission = context['has_delete_permission']
is_popup = context['is_popup']
output = render_to_string('admin_submit_line', {
return {
'onclick_attrib' : (ordered_objects and change
and 'onclick="submitOrderForm();"' or ''),
'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_continue': not is_popup,
'show_save': True
}, context);
context.pop()
return output;
}
class AdminFieldBoundNode(template.Node):
def __init__(self, argument):
self.argument = argument
srdec = inclusion_tag('admin_submit_line', takes_context=True)
submit_row = srdec(submit_row)
#@simple_tag
def field_label(bound_field):
class_names = []
if isinstance(bound_field.field, meta.BooleanField):
class_names.append("vCheckboxLabel")
else:
if not bound_field.field.blank:
class_names.append('required')
if not bound_field.first:
class_names.append('inline')
def render(self, context):
argument_val = template.resolve_variable(self.argument, context)
if (isinstance(argument_val, list)):
bound_fields = argument_val
else:
bound_fields = [argument_val]
add = context['add']
change = context['change']
context.push()
context['bound_fields'] = bound_fields
context['class_names'] = " ".join(self.get_class_names(bound_fields))
t = template_loader.get_template("admin_field")
output = t.render(context)
context.pop()
return output
class_str = class_names and ' class="%s"' % ' '.join(class_names) or ''
return '<label for="%s"%s>%s:</label> ' % \
(bound_field.element_id, class_str,
capfirst(bound_field.field.verbose_name) )
field_label = simple_tag(field_label)
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):
def __init__(self, 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):
@ -100,22 +80,26 @@ class FieldWidgetNode(template.Node):
context.push()
context['bound_field'] = bound_field
klass = bound_field.field.__class__
t = None
while klass:
try:
field_class_name = klass.__name__
template_name = "widget/%s" % \
class_name_to_underscored(field_class_name)
t = template_loader.get_template(template_name)
break
except template.TemplateDoesNotExist:
klass = (len(klass.__bases__) > 0) and klass.__bases__[0] or None
if t == None:
t = template_loader.get_template("widget/default")
output = t.render(context)
if not self.nodelists.has_key(klass):
t = None
while klass:
try:
field_class_name = klass.__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
if t == None:
nodelist = self.default
else:
nodelist = t.nodelist
self.nodelists[klass] = nodelist
output = self.nodelists[klass].render(context)
context.pop()
return output
@ -177,71 +161,38 @@ class EditInlineNode(template.Node):
context['num_headers'] = len(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,)
class FieldLabelNode(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)
class_names = []
if isinstance(bound_field.field, meta.BooleanField):
class_names.append("vCheckboxLabel")
else:
if not bound_field.field.blank:
class_names.append('required')
if not bound_field.first:
class_names.append('inline')
class_str = class_names and ' class="%s"' % ' '.join(class_names) or ''
return '<label for="%s"%s>%s:</label> ' % (bound_field.element_id, class_str, capfirst(bound_field.field.verbose_name) )
class OutputAllNode(template.Node):
def __init__(self, form_fields_var):
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
#@simple_tag
def output_all(form_fields):
return ''.join([str(f) for f in form_fields])
output_all = simple_tag(output_all)
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)
#@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:
return ''
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)
auto_populated_field_script = simple_tag(auto_populated_field_script)
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):
tokens = token.contents.split()
@ -251,14 +202,8 @@ def do_one_arg_tag(node_factory, parser,token):
one_arg_tag_nodes = [
IncludeAdminScriptNode,
AdminFieldBoundNode,
FieldLabelNode,
FieldWidgetNode,
OutputAllNode,
EditInlineNode,
AutoPopulatedFieldScriptNode,
FilterInterfaceScriptMaybeNode,
]
@ -267,9 +212,39 @@ def register_one_arg_tag(node):
parse_func = curry(do_one_arg_tag, node)
template.register_tag(tag_name, parse_func)
for node in one_arg_tag_nodes:
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)