Fixed #17135 -- Made it possible to use decorators (like stringfilter) on template filter functions in combination with auto-escaping. Refs #16726.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17056 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
bebbc9e4a5
commit
d17bc72880
|
@ -11,7 +11,7 @@ from django.utils.tzinfo import LocalTimezone
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def ordinal(value):
|
def ordinal(value):
|
||||||
"""
|
"""
|
||||||
Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd',
|
Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd',
|
||||||
|
@ -25,9 +25,8 @@ def ordinal(value):
|
||||||
if value % 100 in (11, 12, 13): # special case
|
if value % 100 in (11, 12, 13): # special case
|
||||||
return u"%d%s" % (value, suffixes[0])
|
return u"%d%s" % (value, suffixes[0])
|
||||||
return u"%d%s" % (value, suffixes[value % 10])
|
return u"%d%s" % (value, suffixes[value % 10])
|
||||||
ordinal.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def intcomma(value, use_l10n=True):
|
def intcomma(value, use_l10n=True):
|
||||||
"""
|
"""
|
||||||
Converts an integer to a string containing commas every three digits.
|
Converts an integer to a string containing commas every three digits.
|
||||||
|
@ -47,7 +46,6 @@ def intcomma(value, use_l10n=True):
|
||||||
return new
|
return new
|
||||||
else:
|
else:
|
||||||
return intcomma(new, use_l10n)
|
return intcomma(new, use_l10n)
|
||||||
intcomma.is_safe = True
|
|
||||||
|
|
||||||
# A tuple of standard large number to their converters
|
# A tuple of standard large number to their converters
|
||||||
intword_converters = (
|
intword_converters = (
|
||||||
|
@ -97,7 +95,7 @@ intword_converters = (
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def intword(value):
|
def intword(value):
|
||||||
"""
|
"""
|
||||||
Converts a large integer to a friendly text representation. Works best
|
Converts a large integer to a friendly text representation. Works best
|
||||||
|
@ -129,9 +127,8 @@ def intword(value):
|
||||||
new_value = value / float(large_number)
|
new_value = value / float(large_number)
|
||||||
return _check_for_i18n(new_value, *converters(new_value))
|
return _check_for_i18n(new_value, *converters(new_value))
|
||||||
return value
|
return value
|
||||||
intword.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def apnumber(value):
|
def apnumber(value):
|
||||||
"""
|
"""
|
||||||
For numbers 1-9, returns the number spelled out. Otherwise, returns the
|
For numbers 1-9, returns the number spelled out. Otherwise, returns the
|
||||||
|
@ -144,7 +141,6 @@ def apnumber(value):
|
||||||
if not 0 < value < 10:
|
if not 0 < value < 10:
|
||||||
return value
|
return value
|
||||||
return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1]
|
return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1]
|
||||||
apnumber.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def naturalday(value, arg=None):
|
def naturalday(value, arg=None):
|
||||||
|
|
|
@ -18,7 +18,7 @@ from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def textile(value):
|
def textile(value):
|
||||||
try:
|
try:
|
||||||
import textile
|
import textile
|
||||||
|
@ -28,9 +28,8 @@ def textile(value):
|
||||||
return force_unicode(value)
|
return force_unicode(value)
|
||||||
else:
|
else:
|
||||||
return mark_safe(force_unicode(textile.textile(smart_str(value), encoding='utf-8', output='utf-8')))
|
return mark_safe(force_unicode(textile.textile(smart_str(value), encoding='utf-8', output='utf-8')))
|
||||||
textile.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def markdown(value, arg=''):
|
def markdown(value, arg=''):
|
||||||
"""
|
"""
|
||||||
Runs Markdown over a given value, optionally using various
|
Runs Markdown over a given value, optionally using various
|
||||||
|
@ -73,9 +72,8 @@ def markdown(value, arg=''):
|
||||||
return mark_safe(markdown.markdown(force_unicode(value), extensions, safe_mode=safe_mode))
|
return mark_safe(markdown.markdown(force_unicode(value), extensions, safe_mode=safe_mode))
|
||||||
else:
|
else:
|
||||||
return mark_safe(force_unicode(markdown.markdown(smart_str(value))))
|
return mark_safe(force_unicode(markdown.markdown(smart_str(value))))
|
||||||
markdown.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def restructuredtext(value):
|
def restructuredtext(value):
|
||||||
try:
|
try:
|
||||||
from docutils.core import publish_parts
|
from docutils.core import publish_parts
|
||||||
|
@ -87,5 +85,3 @@ def restructuredtext(value):
|
||||||
docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {})
|
docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {})
|
||||||
parts = publish_parts(source=smart_str(value), writer_name="html4css1", settings_overrides=docutils_settings)
|
parts = publish_parts(source=smart_str(value), writer_name="html4css1", settings_overrides=docutils_settings)
|
||||||
return mark_safe(force_unicode(parts["fragment"]))
|
return mark_safe(force_unicode(parts["fragment"]))
|
||||||
restructuredtext.is_safe = True
|
|
||||||
|
|
||||||
|
|
|
@ -1057,30 +1057,43 @@ class Library(object):
|
||||||
self.tags[getattr(func, "_decorated_function", func).__name__] = func
|
self.tags[getattr(func, "_decorated_function", func).__name__] = func
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def filter(self, name=None, filter_func=None):
|
def filter(self, name=None, filter_func=None, **flags):
|
||||||
if name is None and filter_func is None:
|
if name is None and filter_func is None:
|
||||||
# @register.filter()
|
# @register.filter()
|
||||||
return self.filter_function
|
def dec(func):
|
||||||
elif filter_func is None:
|
return self.filter_function(func, **flags)
|
||||||
|
return dec
|
||||||
|
|
||||||
|
elif name is not None and filter_func is None:
|
||||||
if callable(name):
|
if callable(name):
|
||||||
# @register.filter
|
# @register.filter
|
||||||
return self.filter_function(name)
|
return self.filter_function(name, **flags)
|
||||||
else:
|
else:
|
||||||
# @register.filter('somename') or @register.filter(name='somename')
|
# @register.filter('somename') or @register.filter(name='somename')
|
||||||
def dec(func):
|
def dec(func):
|
||||||
return self.filter(name, func)
|
return self.filter(name, func, **flags)
|
||||||
return dec
|
return dec
|
||||||
|
|
||||||
elif name is not None and filter_func is not None:
|
elif name is not None and filter_func is not None:
|
||||||
# register.filter('somename', somefunc)
|
# register.filter('somename', somefunc)
|
||||||
self.filters[name] = filter_func
|
self.filters[name] = filter_func
|
||||||
|
for attr in ('is_safe', 'needs_autoescape'):
|
||||||
|
if attr in flags:
|
||||||
|
value = flags[attr]
|
||||||
|
# set the flag on the filter for FilterExpression.resolve
|
||||||
|
setattr(filter_func, attr, value)
|
||||||
|
# set the flag on the innermost decorated function
|
||||||
|
# for decorators that need it e.g. stringfilter
|
||||||
|
if hasattr(filter_func, "_decorated_function"):
|
||||||
|
setattr(filter_func._decorated_function, attr, value)
|
||||||
return filter_func
|
return filter_func
|
||||||
else:
|
else:
|
||||||
raise InvalidTemplateLibrary("Unsupported arguments to "
|
raise InvalidTemplateLibrary("Unsupported arguments to "
|
||||||
"Library.filter: (%r, %r)", (name, filter_func))
|
"Library.filter: (%r, %r)", (name, filter_func))
|
||||||
|
|
||||||
def filter_function(self, func):
|
def filter_function(self, func, **flags):
|
||||||
self.filters[getattr(func, "_decorated_function", func).__name__] = func
|
name = getattr(func, "_decorated_function", func).__name__
|
||||||
return func
|
return self.filter(name, func, **flags)
|
||||||
|
|
||||||
def simple_tag(self, func=None, takes_context=None, name=None):
|
def simple_tag(self, func=None, takes_context=None, name=None):
|
||||||
def dec(func):
|
def dec(func):
|
||||||
|
|
|
@ -37,23 +37,33 @@ def stringfilter(func):
|
||||||
if args:
|
if args:
|
||||||
args = list(args)
|
args = list(args)
|
||||||
args[0] = force_unicode(args[0])
|
args[0] = force_unicode(args[0])
|
||||||
if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False):
|
if (isinstance(args[0], SafeData) and
|
||||||
|
getattr(_dec._decorated_function, 'is_safe', False)):
|
||||||
return mark_safe(func(*args, **kwargs))
|
return mark_safe(func(*args, **kwargs))
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
# Include a reference to the real function (used to check original
|
# Include a reference to the real function (used to check original
|
||||||
# arguments by the template parser).
|
# arguments by the template parser, and to bear the 'is_safe' attribute
|
||||||
|
# when multiple decorators are applied).
|
||||||
_dec._decorated_function = getattr(func, '_decorated_function', func)
|
_dec._decorated_function = getattr(func, '_decorated_function', func)
|
||||||
|
|
||||||
for attr in ('is_safe', 'needs_autoescape'):
|
for attr in ('is_safe', 'needs_autoescape'):
|
||||||
if hasattr(func, attr):
|
if hasattr(func, attr):
|
||||||
|
import warnings
|
||||||
|
warnings.warn("Setting the %s attribute of a template filter "
|
||||||
|
"function is deprecated; use @register.filter(%s=%s) "
|
||||||
|
"instead" % (attr, attr, getattr(func, attr)),
|
||||||
|
PendingDeprecationWarning)
|
||||||
setattr(_dec, attr, getattr(func, attr))
|
setattr(_dec, attr, getattr(func, attr))
|
||||||
|
|
||||||
return wraps(func)(_dec)
|
return wraps(func)(_dec)
|
||||||
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# STRINGS #
|
# STRINGS #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def addslashes(value):
|
def addslashes(value):
|
||||||
"""
|
"""
|
||||||
|
@ -62,14 +72,12 @@ def addslashes(value):
|
||||||
filter instead.
|
filter instead.
|
||||||
"""
|
"""
|
||||||
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
|
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
|
||||||
addslashes.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def capfirst(value):
|
def capfirst(value):
|
||||||
"""Capitalizes the first character of the value."""
|
"""Capitalizes the first character of the value."""
|
||||||
return value and value[0].upper() + value[1:]
|
return value and value[0].upper() + value[1:]
|
||||||
capfirst.is_safe = True
|
|
||||||
|
|
||||||
@register.filter("escapejs")
|
@register.filter("escapejs")
|
||||||
@stringfilter
|
@stringfilter
|
||||||
|
@ -77,12 +85,11 @@ def escapejs_filter(value):
|
||||||
"""Hex encodes characters for use in JavaScript strings."""
|
"""Hex encodes characters for use in JavaScript strings."""
|
||||||
return escapejs(value)
|
return escapejs(value)
|
||||||
|
|
||||||
@register.filter("fix_ampersands")
|
@register.filter("fix_ampersands", is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def fix_ampersands_filter(value):
|
def fix_ampersands_filter(value):
|
||||||
"""Replaces ampersands with ``&`` entities."""
|
"""Replaces ampersands with ``&`` entities."""
|
||||||
return fix_ampersands(value)
|
return fix_ampersands(value)
|
||||||
fix_ampersands_filter.is_safe = True
|
|
||||||
|
|
||||||
# Values for testing floatformat input against infinity and NaN representations,
|
# Values for testing floatformat input against infinity and NaN representations,
|
||||||
# which differ across platforms and Python versions. Some (i.e. old Windows
|
# which differ across platforms and Python versions. Some (i.e. old Windows
|
||||||
|
@ -96,7 +103,7 @@ neg_inf = -1e200 * 1e200
|
||||||
nan = (1e200 * 1e200) // (1e200 * 1e200)
|
nan = (1e200 * 1e200) // (1e200 * 1e200)
|
||||||
special_floats = [str(pos_inf), str(neg_inf), str(nan)]
|
special_floats = [str(pos_inf), str(neg_inf), str(nan)]
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def floatformat(text, arg=-1):
|
def floatformat(text, arg=-1):
|
||||||
"""
|
"""
|
||||||
Displays a float to a specified number of decimal places.
|
Displays a float to a specified number of decimal places.
|
||||||
|
@ -172,16 +179,14 @@ def floatformat(text, arg=-1):
|
||||||
return mark_safe(formats.number_format(number, abs(p)))
|
return mark_safe(formats.number_format(number, abs(p)))
|
||||||
except InvalidOperation:
|
except InvalidOperation:
|
||||||
return input_val
|
return input_val
|
||||||
floatformat.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def iriencode(value):
|
def iriencode(value):
|
||||||
"""Escapes an IRI value for use in a URL."""
|
"""Escapes an IRI value for use in a URL."""
|
||||||
return force_unicode(iri_to_uri(value))
|
return force_unicode(iri_to_uri(value))
|
||||||
iriencode.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True, needs_autoescape=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def linenumbers(value, autoescape=None):
|
def linenumbers(value, autoescape=None):
|
||||||
"""Displays text with line numbers."""
|
"""Displays text with line numbers."""
|
||||||
|
@ -196,17 +201,14 @@ def linenumbers(value, autoescape=None):
|
||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line))
|
lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line))
|
||||||
return mark_safe(u'\n'.join(lines))
|
return mark_safe(u'\n'.join(lines))
|
||||||
linenumbers.is_safe = True
|
|
||||||
linenumbers.needs_autoescape = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def lower(value):
|
def lower(value):
|
||||||
"""Converts a string into all lowercase."""
|
"""Converts a string into all lowercase."""
|
||||||
return value.lower()
|
return value.lower()
|
||||||
lower.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def make_list(value):
|
def make_list(value):
|
||||||
"""
|
"""
|
||||||
|
@ -216,9 +218,8 @@ def make_list(value):
|
||||||
For a string, it's a list of characters.
|
For a string, it's a list of characters.
|
||||||
"""
|
"""
|
||||||
return list(value)
|
return list(value)
|
||||||
make_list.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def slugify(value):
|
def slugify(value):
|
||||||
"""
|
"""
|
||||||
|
@ -228,9 +229,8 @@ def slugify(value):
|
||||||
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
|
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
|
||||||
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
|
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
|
||||||
return mark_safe(re.sub('[-\s]+', '-', value))
|
return mark_safe(re.sub('[-\s]+', '-', value))
|
||||||
slugify.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def stringformat(value, arg):
|
def stringformat(value, arg):
|
||||||
"""
|
"""
|
||||||
Formats the variable according to the arg, a string formatting specifier.
|
Formats the variable according to the arg, a string formatting specifier.
|
||||||
|
@ -245,17 +245,15 @@ def stringformat(value, arg):
|
||||||
return (u"%" + unicode(arg)) % value
|
return (u"%" + unicode(arg)) % value
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return u""
|
return u""
|
||||||
stringformat.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def title(value):
|
def title(value):
|
||||||
"""Converts a string into titlecase."""
|
"""Converts a string into titlecase."""
|
||||||
t = re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
|
t = re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
|
||||||
return re.sub("\d([A-Z])", lambda m: m.group(0).lower(), t)
|
return re.sub("\d([A-Z])", lambda m: m.group(0).lower(), t)
|
||||||
title.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def truncatechars(value, arg):
|
def truncatechars(value, arg):
|
||||||
"""
|
"""
|
||||||
|
@ -268,9 +266,8 @@ def truncatechars(value, arg):
|
||||||
except ValueError: # Invalid literal for int().
|
except ValueError: # Invalid literal for int().
|
||||||
return value # Fail silently.
|
return value # Fail silently.
|
||||||
return Truncator(value).chars(length)
|
return Truncator(value).chars(length)
|
||||||
truncatechars.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def truncatewords(value, arg):
|
def truncatewords(value, arg):
|
||||||
"""
|
"""
|
||||||
|
@ -285,9 +282,8 @@ def truncatewords(value, arg):
|
||||||
except ValueError: # Invalid literal for int().
|
except ValueError: # Invalid literal for int().
|
||||||
return value # Fail silently.
|
return value # Fail silently.
|
||||||
return Truncator(value).words(length, truncate=' ...')
|
return Truncator(value).words(length, truncate=' ...')
|
||||||
truncatewords.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def truncatewords_html(value, arg):
|
def truncatewords_html(value, arg):
|
||||||
"""
|
"""
|
||||||
|
@ -302,16 +298,14 @@ def truncatewords_html(value, arg):
|
||||||
except ValueError: # invalid literal for int()
|
except ValueError: # invalid literal for int()
|
||||||
return value # Fail silently.
|
return value # Fail silently.
|
||||||
return Truncator(value).words(length, html=True, truncate=' ...')
|
return Truncator(value).words(length, html=True, truncate=' ...')
|
||||||
truncatewords_html.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def upper(value):
|
def upper(value):
|
||||||
"""Converts a string into all uppercase."""
|
"""Converts a string into all uppercase."""
|
||||||
return value.upper()
|
return value.upper()
|
||||||
upper.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def urlencode(value, safe=None):
|
def urlencode(value, safe=None):
|
||||||
"""
|
"""
|
||||||
|
@ -326,17 +320,14 @@ def urlencode(value, safe=None):
|
||||||
if safe is not None:
|
if safe is not None:
|
||||||
kwargs['safe'] = safe
|
kwargs['safe'] = safe
|
||||||
return urlquote(value, **kwargs)
|
return urlquote(value, **kwargs)
|
||||||
urlencode.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True, needs_autoescape=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def urlize(value, autoescape=None):
|
def urlize(value, autoescape=None):
|
||||||
"""Converts URLs in plain text into clickable links."""
|
"""Converts URLs in plain text into clickable links."""
|
||||||
return mark_safe(urlize_impl(value, nofollow=True, autoescape=autoescape))
|
return mark_safe(urlize_impl(value, nofollow=True, autoescape=autoescape))
|
||||||
urlize.is_safe = True
|
|
||||||
urlize.needs_autoescape = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True, needs_autoescape=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def urlizetrunc(value, limit, autoescape=None):
|
def urlizetrunc(value, limit, autoescape=None):
|
||||||
"""
|
"""
|
||||||
|
@ -347,17 +338,14 @@ def urlizetrunc(value, limit, autoescape=None):
|
||||||
"""
|
"""
|
||||||
return mark_safe(urlize_impl(value, trim_url_limit=int(limit), nofollow=True,
|
return mark_safe(urlize_impl(value, trim_url_limit=int(limit), nofollow=True,
|
||||||
autoescape=autoescape))
|
autoescape=autoescape))
|
||||||
urlizetrunc.is_safe = True
|
|
||||||
urlizetrunc.needs_autoescape = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def wordcount(value):
|
def wordcount(value):
|
||||||
"""Returns the number of words."""
|
"""Returns the number of words."""
|
||||||
return len(value.split())
|
return len(value.split())
|
||||||
wordcount.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def wordwrap(value, arg):
|
def wordwrap(value, arg):
|
||||||
"""
|
"""
|
||||||
|
@ -366,9 +354,8 @@ def wordwrap(value, arg):
|
||||||
Argument: number of characters to wrap the text at.
|
Argument: number of characters to wrap the text at.
|
||||||
"""
|
"""
|
||||||
return wrap(value, int(arg))
|
return wrap(value, int(arg))
|
||||||
wordwrap.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def ljust(value, arg):
|
def ljust(value, arg):
|
||||||
"""
|
"""
|
||||||
|
@ -377,9 +364,8 @@ def ljust(value, arg):
|
||||||
Argument: field size.
|
Argument: field size.
|
||||||
"""
|
"""
|
||||||
return value.ljust(int(arg))
|
return value.ljust(int(arg))
|
||||||
ljust.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def rjust(value, arg):
|
def rjust(value, arg):
|
||||||
"""
|
"""
|
||||||
|
@ -388,14 +374,12 @@ def rjust(value, arg):
|
||||||
Argument: field size.
|
Argument: field size.
|
||||||
"""
|
"""
|
||||||
return value.rjust(int(arg))
|
return value.rjust(int(arg))
|
||||||
rjust.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def center(value, arg):
|
def center(value, arg):
|
||||||
"""Centers the value in a field of a given width."""
|
"""Centers the value in a field of a given width."""
|
||||||
return value.center(int(arg))
|
return value.center(int(arg))
|
||||||
center.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
@stringfilter
|
@stringfilter
|
||||||
|
@ -413,16 +397,15 @@ def cut(value, arg):
|
||||||
# HTML STRINGS #
|
# HTML STRINGS #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
@register.filter("escape")
|
@register.filter("escape", is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def escape_filter(value):
|
def escape_filter(value):
|
||||||
"""
|
"""
|
||||||
Marks the value as a string that should not be auto-escaped.
|
Marks the value as a string that should not be auto-escaped.
|
||||||
"""
|
"""
|
||||||
return mark_for_escaping(value)
|
return mark_for_escaping(value)
|
||||||
escape_filter.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def force_escape(value):
|
def force_escape(value):
|
||||||
"""
|
"""
|
||||||
|
@ -431,9 +414,8 @@ def force_escape(value):
|
||||||
possible escaping).
|
possible escaping).
|
||||||
"""
|
"""
|
||||||
return mark_safe(escape(value))
|
return mark_safe(escape(value))
|
||||||
force_escape.is_safe = True
|
|
||||||
|
|
||||||
@register.filter("linebreaks")
|
@register.filter("linebreaks", is_safe=True, needs_autoescape=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def linebreaks_filter(value, autoescape=None):
|
def linebreaks_filter(value, autoescape=None):
|
||||||
"""
|
"""
|
||||||
|
@ -443,10 +425,8 @@ def linebreaks_filter(value, autoescape=None):
|
||||||
"""
|
"""
|
||||||
autoescape = autoescape and not isinstance(value, SafeData)
|
autoescape = autoescape and not isinstance(value, SafeData)
|
||||||
return mark_safe(linebreaks(value, autoescape))
|
return mark_safe(linebreaks(value, autoescape))
|
||||||
linebreaks_filter.is_safe = True
|
|
||||||
linebreaks_filter.needs_autoescape = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True, needs_autoescape=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def linebreaksbr(value, autoescape=None):
|
def linebreaksbr(value, autoescape=None):
|
||||||
"""
|
"""
|
||||||
|
@ -458,19 +438,16 @@ def linebreaksbr(value, autoescape=None):
|
||||||
if autoescape:
|
if autoescape:
|
||||||
value = escape(value)
|
value = escape(value)
|
||||||
return mark_safe(value.replace('\n', '<br />'))
|
return mark_safe(value.replace('\n', '<br />'))
|
||||||
linebreaksbr.is_safe = True
|
|
||||||
linebreaksbr.needs_autoescape = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def safe(value):
|
def safe(value):
|
||||||
"""
|
"""
|
||||||
Marks the value as a string that should not be auto-escaped.
|
Marks the value as a string that should not be auto-escaped.
|
||||||
"""
|
"""
|
||||||
return mark_safe(value)
|
return mark_safe(value)
|
||||||
safe.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def safeseq(value):
|
def safeseq(value):
|
||||||
"""
|
"""
|
||||||
A "safe" filter for sequences. Marks each element in the sequence,
|
A "safe" filter for sequences. Marks each element in the sequence,
|
||||||
|
@ -478,9 +455,8 @@ def safeseq(value):
|
||||||
with the results.
|
with the results.
|
||||||
"""
|
"""
|
||||||
return [mark_safe(force_unicode(obj)) for obj in value]
|
return [mark_safe(force_unicode(obj)) for obj in value]
|
||||||
safeseq.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def removetags(value, tags):
|
def removetags(value, tags):
|
||||||
"""Removes a space separated list of [X]HTML tags from the output."""
|
"""Removes a space separated list of [X]HTML tags from the output."""
|
||||||
|
@ -491,47 +467,42 @@ def removetags(value, tags):
|
||||||
value = starttag_re.sub(u'', value)
|
value = starttag_re.sub(u'', value)
|
||||||
value = endtag_re.sub(u'', value)
|
value = endtag_re.sub(u'', value)
|
||||||
return value
|
return value
|
||||||
removetags.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
@stringfilter
|
@stringfilter
|
||||||
def striptags(value):
|
def striptags(value):
|
||||||
"""Strips all [X]HTML tags."""
|
"""Strips all [X]HTML tags."""
|
||||||
return strip_tags(value)
|
return strip_tags(value)
|
||||||
striptags.is_safe = True
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# LISTS #
|
# LISTS #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def dictsort(value, arg):
|
def dictsort(value, arg):
|
||||||
"""
|
"""
|
||||||
Takes a list of dicts, returns that list sorted by the property given in
|
Takes a list of dicts, returns that list sorted by the property given in
|
||||||
the argument.
|
the argument.
|
||||||
"""
|
"""
|
||||||
return sorted(value, key=Variable(arg).resolve)
|
return sorted(value, key=Variable(arg).resolve)
|
||||||
dictsort.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def dictsortreversed(value, arg):
|
def dictsortreversed(value, arg):
|
||||||
"""
|
"""
|
||||||
Takes a list of dicts, returns that list sorted in reverse order by the
|
Takes a list of dicts, returns that list sorted in reverse order by the
|
||||||
property given in the argument.
|
property given in the argument.
|
||||||
"""
|
"""
|
||||||
return sorted(value, key=Variable(arg).resolve, reverse=True)
|
return sorted(value, key=Variable(arg).resolve, reverse=True)
|
||||||
dictsortreversed.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def first(value):
|
def first(value):
|
||||||
"""Returns the first item in a list."""
|
"""Returns the first item in a list."""
|
||||||
try:
|
try:
|
||||||
return value[0]
|
return value[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return u''
|
return u''
|
||||||
first.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True, needs_autoescape=True)
|
||||||
def join(value, arg, autoescape=None):
|
def join(value, arg, autoescape=None):
|
||||||
"""
|
"""
|
||||||
Joins a list with a string, like Python's ``str.join(list)``.
|
Joins a list with a string, like Python's ``str.join(list)``.
|
||||||
|
@ -544,43 +515,37 @@ def join(value, arg, autoescape=None):
|
||||||
except AttributeError: # fail silently but nicely
|
except AttributeError: # fail silently but nicely
|
||||||
return value
|
return value
|
||||||
return mark_safe(data)
|
return mark_safe(data)
|
||||||
join.is_safe = True
|
|
||||||
join.needs_autoescape = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def last(value):
|
def last(value):
|
||||||
"Returns the last item in a list"
|
"Returns the last item in a list"
|
||||||
try:
|
try:
|
||||||
return value[-1]
|
return value[-1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return u''
|
return u''
|
||||||
last.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def length(value):
|
def length(value):
|
||||||
"""Returns the length of the value - useful for lists."""
|
"""Returns the length of the value - useful for lists."""
|
||||||
try:
|
try:
|
||||||
return len(value)
|
return len(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return ''
|
return ''
|
||||||
length.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def length_is(value, arg):
|
def length_is(value, arg):
|
||||||
"""Returns a boolean of whether the value's length is the argument."""
|
"""Returns a boolean of whether the value's length is the argument."""
|
||||||
try:
|
try:
|
||||||
return len(value) == int(arg)
|
return len(value) == int(arg)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return ''
|
return ''
|
||||||
length_is.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def random(value):
|
def random(value):
|
||||||
"""Returns a random item from the list."""
|
"""Returns a random item from the list."""
|
||||||
return random_module.choice(value)
|
return random_module.choice(value)
|
||||||
random.is_safe = True
|
|
||||||
|
|
||||||
@register.filter("slice")
|
@register.filter("slice", is_safe=True)
|
||||||
def slice_filter(value, arg):
|
def slice_filter(value, arg):
|
||||||
"""
|
"""
|
||||||
Returns a slice of the list.
|
Returns a slice of the list.
|
||||||
|
@ -600,9 +565,8 @@ def slice_filter(value, arg):
|
||||||
|
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return value # Fail silently.
|
return value # Fail silently.
|
||||||
slice_filter.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True, needs_autoescape=True)
|
||||||
def unordered_list(value, autoescape=None):
|
def unordered_list(value, autoescape=None):
|
||||||
"""
|
"""
|
||||||
Recursively takes a self-nested list and returns an HTML unordered list --
|
Recursively takes a self-nested list and returns an HTML unordered list --
|
||||||
|
@ -688,14 +652,12 @@ def unordered_list(value, autoescape=None):
|
||||||
return '\n'.join(output)
|
return '\n'.join(output)
|
||||||
value, converted = convert_old_style_list(value)
|
value, converted = convert_old_style_list(value)
|
||||||
return mark_safe(_helper(value))
|
return mark_safe(_helper(value))
|
||||||
unordered_list.is_safe = True
|
|
||||||
unordered_list.needs_autoescape = True
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# INTEGERS #
|
# INTEGERS #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def add(value, arg):
|
def add(value, arg):
|
||||||
"""Adds the arg to the value."""
|
"""Adds the arg to the value."""
|
||||||
try:
|
try:
|
||||||
|
@ -705,9 +667,8 @@ def add(value, arg):
|
||||||
return value + arg
|
return value + arg
|
||||||
except Exception:
|
except Exception:
|
||||||
return ''
|
return ''
|
||||||
add.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def get_digit(value, arg):
|
def get_digit(value, arg):
|
||||||
"""
|
"""
|
||||||
Given a whole number, returns the requested digit of it, where 1 is the
|
Given a whole number, returns the requested digit of it, where 1 is the
|
||||||
|
@ -726,13 +687,12 @@ def get_digit(value, arg):
|
||||||
return int(str(value)[-arg])
|
return int(str(value)[-arg])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return 0
|
return 0
|
||||||
get_digit.is_safe = False
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# DATES #
|
# DATES #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def date(value, arg=None):
|
def date(value, arg=None):
|
||||||
"""Formats a date according to the given format."""
|
"""Formats a date according to the given format."""
|
||||||
if not value:
|
if not value:
|
||||||
|
@ -746,9 +706,8 @@ def date(value, arg=None):
|
||||||
return format(value, arg)
|
return format(value, arg)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return ''
|
return ''
|
||||||
date.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def time(value, arg=None):
|
def time(value, arg=None):
|
||||||
"""Formats a time according to the given format."""
|
"""Formats a time according to the given format."""
|
||||||
if value in (None, u''):
|
if value in (None, u''):
|
||||||
|
@ -762,9 +721,8 @@ def time(value, arg=None):
|
||||||
return time_format(value, arg)
|
return time_format(value, arg)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return ''
|
return ''
|
||||||
time.is_safe = False
|
|
||||||
|
|
||||||
@register.filter("timesince")
|
@register.filter("timesince", is_safe=False)
|
||||||
def timesince_filter(value, arg=None):
|
def timesince_filter(value, arg=None):
|
||||||
"""Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
|
"""Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
|
||||||
if not value:
|
if not value:
|
||||||
|
@ -775,9 +733,8 @@ def timesince_filter(value, arg=None):
|
||||||
return timesince(value)
|
return timesince(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return u''
|
return u''
|
||||||
timesince_filter.is_safe = False
|
|
||||||
|
|
||||||
@register.filter("timeuntil")
|
@register.filter("timeuntil", is_safe=False)
|
||||||
def timeuntil_filter(value, arg=None):
|
def timeuntil_filter(value, arg=None):
|
||||||
"""Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
|
"""Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
|
||||||
if not value:
|
if not value:
|
||||||
|
@ -786,33 +743,29 @@ def timeuntil_filter(value, arg=None):
|
||||||
return timeuntil(value, arg)
|
return timeuntil(value, arg)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return u''
|
return u''
|
||||||
timeuntil_filter.is_safe = False
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# LOGIC #
|
# LOGIC #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def default(value, arg):
|
def default(value, arg):
|
||||||
"""If value is unavailable, use given default."""
|
"""If value is unavailable, use given default."""
|
||||||
return value or arg
|
return value or arg
|
||||||
default.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def default_if_none(value, arg):
|
def default_if_none(value, arg):
|
||||||
"""If value is None, use given default."""
|
"""If value is None, use given default."""
|
||||||
if value is None:
|
if value is None:
|
||||||
return arg
|
return arg
|
||||||
return value
|
return value
|
||||||
default_if_none.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def divisibleby(value, arg):
|
def divisibleby(value, arg):
|
||||||
"""Returns True if the value is devisible by the argument."""
|
"""Returns True if the value is devisible by the argument."""
|
||||||
return int(value) % int(arg) == 0
|
return int(value) % int(arg) == 0
|
||||||
divisibleby.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def yesno(value, arg=None):
|
def yesno(value, arg=None):
|
||||||
"""
|
"""
|
||||||
Given a string mapping values for true, false and (optionally) None,
|
Given a string mapping values for true, false and (optionally) None,
|
||||||
|
@ -843,13 +796,12 @@ def yesno(value, arg=None):
|
||||||
if value:
|
if value:
|
||||||
return yes
|
return yes
|
||||||
return no
|
return no
|
||||||
yesno.is_safe = False
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# MISC #
|
# MISC #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def filesizeformat(bytes):
|
def filesizeformat(bytes):
|
||||||
"""
|
"""
|
||||||
Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
|
Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
|
||||||
|
@ -873,9 +825,8 @@ def filesizeformat(bytes):
|
||||||
if bytes < 1024 * 1024 * 1024 * 1024 * 1024:
|
if bytes < 1024 * 1024 * 1024 * 1024 * 1024:
|
||||||
return ugettext("%s TB") % filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024))
|
return ugettext("%s TB") % filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024))
|
||||||
return ugettext("%s PB") % filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024 * 1024))
|
return ugettext("%s PB") % filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024 * 1024))
|
||||||
filesizeformat.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def pluralize(value, arg=u's'):
|
def pluralize(value, arg=u's'):
|
||||||
"""
|
"""
|
||||||
Returns a plural suffix if the value is not 1. By default, 's' is used as
|
Returns a plural suffix if the value is not 1. By default, 's' is used as
|
||||||
|
@ -918,19 +869,16 @@ def pluralize(value, arg=u's'):
|
||||||
except TypeError: # len() of unsized object.
|
except TypeError: # len() of unsized object.
|
||||||
pass
|
pass
|
||||||
return singular_suffix
|
return singular_suffix
|
||||||
pluralize.is_safe = False
|
|
||||||
|
|
||||||
@register.filter("phone2numeric")
|
@register.filter("phone2numeric", is_safe=True)
|
||||||
def phone2numeric_filter(value):
|
def phone2numeric_filter(value):
|
||||||
"""Takes a phone number and converts it in to its numerical equivalent."""
|
"""Takes a phone number and converts it in to its numerical equivalent."""
|
||||||
return phone2numeric(value)
|
return phone2numeric(value)
|
||||||
phone2numeric_filter.is_safe = True
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def pprint(value):
|
def pprint(value):
|
||||||
"""A wrapper around pprint.pprint -- for debugging, really."""
|
"""A wrapper around pprint.pprint -- for debugging, really."""
|
||||||
try:
|
try:
|
||||||
return pformat(value)
|
return pformat(value)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
return u"Error in formatting: %s" % force_unicode(e, errors="replace")
|
return u"Error in formatting: %s" % force_unicode(e, errors="replace")
|
||||||
pprint.is_safe = True
|
|
||||||
|
|
|
@ -5,23 +5,21 @@ from django.utils.encoding import force_unicode
|
||||||
|
|
||||||
register = Library()
|
register = Library()
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def localize(value):
|
def localize(value):
|
||||||
"""
|
"""
|
||||||
Forces a value to be rendered as a localized value,
|
Forces a value to be rendered as a localized value,
|
||||||
regardless of the value of ``settings.USE_L10N``.
|
regardless of the value of ``settings.USE_L10N``.
|
||||||
"""
|
"""
|
||||||
return force_unicode(formats.localize(value, use_l10n=True))
|
return force_unicode(formats.localize(value, use_l10n=True))
|
||||||
localize.is_safe = False
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=False)
|
||||||
def unlocalize(value):
|
def unlocalize(value):
|
||||||
"""
|
"""
|
||||||
Forces a value to be rendered as a non-localized value,
|
Forces a value to be rendered as a non-localized value,
|
||||||
regardless of the value of ``settings.USE_L10N``.
|
regardless of the value of ``settings.USE_L10N``.
|
||||||
"""
|
"""
|
||||||
return force_unicode(value)
|
return force_unicode(value)
|
||||||
unlocalize.is_safe = False
|
|
||||||
|
|
||||||
class LocalizeNode(Node):
|
class LocalizeNode(Node):
|
||||||
def __init__(self, nodelist, use_l10n):
|
def __init__(self, nodelist, use_l10n):
|
||||||
|
|
|
@ -143,6 +143,10 @@ You can use ``register.filter()`` as a decorator instead:
|
||||||
If you leave off the ``name`` argument, as in the second example above, Django
|
If you leave off the ``name`` argument, as in the second example above, Django
|
||||||
will use the function's name as the filter name.
|
will use the function's name as the filter name.
|
||||||
|
|
||||||
|
Finally, ``register.filter()`` also accepts two keyword arguments, ``is_safe``
|
||||||
|
and ``needs_autoescape``, described in :ref:`filters and auto-escaping
|
||||||
|
<filters-auto-escaping>` below.
|
||||||
|
|
||||||
Template filters that expect strings
|
Template filters that expect strings
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -166,6 +170,8 @@ This way, you'll be able to pass, say, an integer to this filter, and it
|
||||||
won't cause an ``AttributeError`` (because integers don't have ``lower()``
|
won't cause an ``AttributeError`` (because integers don't have ``lower()``
|
||||||
methods).
|
methods).
|
||||||
|
|
||||||
|
.. _filters-auto-escaping:
|
||||||
|
|
||||||
Filters and auto-escaping
|
Filters and auto-escaping
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -206,17 +212,16 @@ Template filter code falls into one of two situations:
|
||||||
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
||||||
``'``, ``"`` or ``&``) into the result that were not already present. In
|
``'``, ``"`` or ``&``) into the result that were not already present. In
|
||||||
this case, you can let Django take care of all the auto-escaping
|
this case, you can let Django take care of all the auto-escaping
|
||||||
handling for you. All you need to do is put the ``is_safe`` attribute on
|
handling for you. All you need to do is set the ``is_safe`` flag to ``True``
|
||||||
your filter function and set it to ``True``, like so:
|
when you register your filter function, like so:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def myfilter(value):
|
def myfilter(value):
|
||||||
return value
|
return value
|
||||||
myfilter.is_safe = True
|
|
||||||
|
|
||||||
This attribute tells Django that if a "safe" string is passed into your
|
This flag tells Django that if a "safe" string is passed into your
|
||||||
filter, the result will still be "safe" and if a non-safe string is
|
filter, the result will still be "safe" and if a non-safe string is
|
||||||
passed in, Django will automatically escape it, if necessary.
|
passed in, Django will automatically escape it, if necessary.
|
||||||
|
|
||||||
|
@ -236,17 +241,16 @@ Template filter code falls into one of two situations:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@register.filter
|
@register.filter(is_safe=True)
|
||||||
def add_xx(value):
|
def add_xx(value):
|
||||||
return '%sxx' % value
|
return '%sxx' % value
|
||||||
add_xx.is_safe = True
|
|
||||||
|
|
||||||
When this filter is used in a template where auto-escaping is enabled,
|
When this filter is used in a template where auto-escaping is enabled,
|
||||||
Django will escape the output whenever the input is not already marked
|
Django will escape the output whenever the input is not already marked
|
||||||
as "safe".
|
as "safe".
|
||||||
|
|
||||||
By default, ``is_safe`` defaults to ``False``, and you can omit it from
|
By default, ``is_safe`` is ``False``, and you can omit it from any filters
|
||||||
any filters where it isn't required.
|
where it isn't required.
|
||||||
|
|
||||||
Be careful when deciding if your filter really does leave safe strings
|
Be careful when deciding if your filter really does leave safe strings
|
||||||
as safe. If you're *removing* characters, you might inadvertently leave
|
as safe. If you're *removing* characters, you might inadvertently leave
|
||||||
|
@ -279,12 +283,12 @@ Template filter code falls into one of two situations:
|
||||||
can operate in templates where auto-escaping is either on or off in
|
can operate in templates where auto-escaping is either on or off in
|
||||||
order to make things easier for your template authors.
|
order to make things easier for your template authors.
|
||||||
|
|
||||||
In order for your filter to know the current auto-escaping state, set
|
In order for your filter to know the current auto-escaping state, set the
|
||||||
the ``needs_autoescape`` attribute to ``True`` on your function. (If you
|
``needs_autoescape`` flag to ``True`` when you register your filter function.
|
||||||
don't specify this attribute, it defaults to ``False``). This attribute
|
(If you don't specify this flag, it defaults to ``False``). This flag tells
|
||||||
tells Django that your filter function wants to be passed an extra
|
Django that your filter function wants to be passed an extra keyword
|
||||||
keyword argument, called ``autoescape``, that is ``True`` if
|
argument, called ``autoescape``, that is ``True`` if auto-escaping is in
|
||||||
auto-escaping is in effect and ``False`` otherwise.
|
effect and ``False`` otherwise.
|
||||||
|
|
||||||
For example, let's write a filter that emphasizes the first character of
|
For example, let's write a filter that emphasizes the first character of
|
||||||
a string:
|
a string:
|
||||||
|
@ -294,6 +298,7 @@ Template filter code falls into one of two situations:
|
||||||
from django.utils.html import conditional_escape
|
from django.utils.html import conditional_escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
@register.filter(needs_autoescape=True)
|
||||||
def initial_letter_filter(text, autoescape=None):
|
def initial_letter_filter(text, autoescape=None):
|
||||||
first, other = text[0], text[1:]
|
first, other = text[0], text[1:]
|
||||||
if autoescape:
|
if autoescape:
|
||||||
|
@ -302,27 +307,45 @@ Template filter code falls into one of two situations:
|
||||||
esc = lambda x: x
|
esc = lambda x: x
|
||||||
result = '<strong>%s</strong>%s' % (esc(first), esc(other))
|
result = '<strong>%s</strong>%s' % (esc(first), esc(other))
|
||||||
return mark_safe(result)
|
return mark_safe(result)
|
||||||
initial_letter_filter.needs_autoescape = True
|
|
||||||
|
|
||||||
The ``needs_autoescape`` attribute on the filter function and the
|
The ``needs_autoescape`` flag and the ``autoescape`` keyword argument mean
|
||||||
``autoescape`` keyword argument mean that our function will know whether
|
that our function will know whether automatic escaping is in effect when the
|
||||||
automatic escaping is in effect when the filter is called. We use
|
filter is called. We use ``autoescape`` to decide whether the input data
|
||||||
``autoescape`` to decide whether the input data needs to be passed
|
needs to be passed through ``django.utils.html.conditional_escape`` or not.
|
||||||
through ``django.utils.html.conditional_escape`` or not. (In the latter
|
(In the latter case, we just use the identity function as the "escape"
|
||||||
case, we just use the identity function as the "escape" function.) The
|
function.) The ``conditional_escape()`` function is like ``escape()`` except
|
||||||
``conditional_escape()`` function is like ``escape()`` except it only
|
it only escapes input that is **not** a ``SafeData`` instance. If a
|
||||||
escapes input that is **not** a ``SafeData`` instance. If a ``SafeData``
|
``SafeData`` instance is passed to ``conditional_escape()``, the data is
|
||||||
instance is passed to ``conditional_escape()``, the data is returned
|
returned unchanged.
|
||||||
unchanged.
|
|
||||||
|
|
||||||
Finally, in the above example, we remember to mark the result as safe
|
Finally, in the above example, we remember to mark the result as safe
|
||||||
so that our HTML is inserted directly into the template without further
|
so that our HTML is inserted directly into the template without further
|
||||||
escaping.
|
escaping.
|
||||||
|
|
||||||
There's no need to worry about the ``is_safe`` attribute in this case
|
There's no need to worry about the ``is_safe`` flag in this case
|
||||||
(although including it wouldn't hurt anything). Whenever you manually
|
(although including it wouldn't hurt anything). Whenever you manually
|
||||||
handle the auto-escaping issues and return a safe string, the
|
handle the auto-escaping issues and return a safe string, the
|
||||||
``is_safe`` attribute won't change anything either way.
|
``is_safe`` flag won't change anything either way.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.4
|
||||||
|
|
||||||
|
``is_safe`` and ``needs_autoescape`` used to be attributes of the filter
|
||||||
|
function; this syntax is deprecated.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def myfilter(value):
|
||||||
|
return value
|
||||||
|
myfilter.is_safe = True
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def initial_letter_filter(text, autoescape=None):
|
||||||
|
# ...
|
||||||
|
return mark_safe(result)
|
||||||
|
initial_letter_filter.needs_autoescape = True
|
||||||
|
|
||||||
Writing custom template tags
|
Writing custom template tags
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
|
@ -251,6 +251,9 @@ these changes.
|
||||||
:mod:`django.core.management`. This also means that the old (pre-1.4)
|
:mod:`django.core.management`. This also means that the old (pre-1.4)
|
||||||
style of :file:`manage.py` file will no longer work.
|
style of :file:`manage.py` file will no longer work.
|
||||||
|
|
||||||
|
* Setting the ``is_safe`` and ``needs_autoescape`` flags as attributes of
|
||||||
|
template filter functions will no longer be supported.
|
||||||
|
|
||||||
2.0
|
2.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,10 @@ from django.template.loader import get_template
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
@stringfilter
|
||||||
def trim(value, num):
|
def trim(value, num):
|
||||||
return value[:num]
|
return value[:num]
|
||||||
trim = stringfilter(trim)
|
|
||||||
|
|
||||||
register.filter(trim)
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def no_params():
|
def no_params():
|
||||||
|
@ -303,4 +302,4 @@ assignment_unlimited_args_kwargs.anything = "Expected assignment_unlimited_args_
|
||||||
def assignment_tag_without_context_parameter(arg):
|
def assignment_tag_without_context_parameter(arg):
|
||||||
"""Expected assignment_tag_without_context_parameter __doc__"""
|
"""Expected assignment_tag_without_context_parameter __doc__"""
|
||||||
return "Expected result"
|
return "Expected result"
|
||||||
assignment_tag_without_context_parameter.anything = "Expected assignment_tag_without_context_parameter __dict__"
|
assignment_tag_without_context_parameter.anything = "Expected assignment_tag_without_context_parameter __dict__"
|
||||||
|
|
Loading…
Reference in New Issue