mirror of
https://github.com/django/django.git
synced 2025-07-05 18:29:11 +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:
parent
555502e208
commit
e3f3d4e86f
@ -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 %}
|
||||
|
||||
|
@ -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 #
|
||||
|
@ -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):
|
||||
|
70
django/core/template_decorators.py
Normal file
70
django/core/template_decorators.py
Normal 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
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user