/build/lib.linux-x86_64-2.7/django/template/defaultfilters.py
Python | 896 lines | 834 code | 22 blank | 40 comment | 11 complexity | 2a9e2d4661678f0311fedbea59af70f3 MD5 | raw file
Possible License(s): BSD-3-Clause
- """Default variable filters."""
- import re
- import random as random_module
- import unicodedata
- from decimal import Decimal, InvalidOperation, Context, ROUND_HALF_UP
- from functools import wraps
- from pprint import pformat
- from django.template.base import Variable, Library, VariableDoesNotExist
- from django.conf import settings
- from django.utils import formats
- from django.utils.dateformat import format, time_format
- from django.utils.encoding import force_unicode, iri_to_uri
- from django.utils.html import (conditional_escape, escapejs, fix_ampersands,
- escape, urlize as urlize_impl, linebreaks, strip_tags)
- from django.utils.http import urlquote
- from django.utils.text import Truncator, wrap, phone2numeric
- from django.utils.safestring import mark_safe, SafeData, mark_for_escaping
- from django.utils.timesince import timesince, timeuntil
- from django.utils.translation import ugettext, ungettext
- from django.utils.text import normalize_newlines
- register = Library()
- #######################
- # STRING DECORATOR #
- #######################
- def stringfilter(func):
- """
- Decorator for filters which should only receive unicode objects. The object
- passed as the first positional argument will be converted to a unicode
- object.
- """
- def _dec(*args, **kwargs):
- if args:
- args = list(args)
- args[0] = force_unicode(args[0])
- if (isinstance(args[0], SafeData) and
- getattr(_dec._decorated_function, 'is_safe', False)):
- return mark_safe(func(*args, **kwargs))
- return func(*args, **kwargs)
- # Include a reference to the real function (used to check original
- # 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)
- for attr in ('is_safe', 'needs_autoescape'):
- 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))
- return wraps(func)(_dec)
- ###################
- # STRINGS #
- ###################
- @register.filter(is_safe=True)
- @stringfilter
- def addslashes(value):
- """
- Adds slashes before quotes. Useful for escaping strings in CSV, for
- example. Less useful for escaping JavaScript; use the ``escapejs``
- filter instead.
- """
- return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
- @register.filter(is_safe=True)
- @stringfilter
- def capfirst(value):
- """Capitalizes the first character of the value."""
- return value and value[0].upper() + value[1:]
- @register.filter("escapejs")
- @stringfilter
- def escapejs_filter(value):
- """Hex encodes characters for use in JavaScript strings."""
- return escapejs(value)
- @register.filter("fix_ampersands", is_safe=True)
- @stringfilter
- def fix_ampersands_filter(value):
- """Replaces ampersands with ``&`` entities."""
- return fix_ampersands(value)
- # Values for testing floatformat input against infinity and NaN representations,
- # which differ across platforms and Python versions. Some (i.e. old Windows
- # ones) are not recognized by Decimal but we want to return them unchanged vs.
- # returning an empty string as we do for completley invalid input. Note these
- # need to be built up from values that are not inf/nan, since inf/nan values do
- # not reload properly from .pyc files on Windows prior to some level of Python 2.5
- # (see Python Issue757815 and Issue1080440).
- pos_inf = 1e200 * 1e200
- neg_inf = -1e200 * 1e200
- nan = (1e200 * 1e200) // (1e200 * 1e200)
- special_floats = [str(pos_inf), str(neg_inf), str(nan)]
- @register.filter(is_safe=True)
- def floatformat(text, arg=-1):
- """
- Displays a float to a specified number of decimal places.
- If called without an argument, it displays the floating point number with
- one decimal place -- but only if there's a decimal place to be displayed:
- * num1 = 34.23234
- * num2 = 34.00000
- * num3 = 34.26000
- * {{ num1|floatformat }} displays "34.2"
- * {{ num2|floatformat }} displays "34"
- * {{ num3|floatformat }} displays "34.3"
- If arg is positive, it will always display exactly arg number of decimal
- places:
- * {{ num1|floatformat:3 }} displays "34.232"
- * {{ num2|floatformat:3 }} displays "34.000"
- * {{ num3|floatformat:3 }} displays "34.260"
- If arg is negative, it will display arg number of decimal places -- but
- only if there are places to be displayed:
- * {{ num1|floatformat:"-3" }} displays "34.232"
- * {{ num2|floatformat:"-3" }} displays "34"
- * {{ num3|floatformat:"-3" }} displays "34.260"
- If the input float is infinity or NaN, the (platform-dependent) string
- representation of that value will be displayed.
- """
- try:
- input_val = force_unicode(text)
- d = Decimal(input_val)
- except UnicodeEncodeError:
- return u''
- except InvalidOperation:
- if input_val in special_floats:
- return input_val
- try:
- d = Decimal(force_unicode(float(text)))
- except (ValueError, InvalidOperation, TypeError, UnicodeEncodeError):
- return u''
- try:
- p = int(arg)
- except ValueError:
- return input_val
- try:
- m = int(d) - d
- except (ValueError, OverflowError, InvalidOperation):
- return input_val
- if not m and p < 0:
- return mark_safe(formats.number_format(u'%d' % (int(d)), 0))
- if p == 0:
- exp = Decimal(1)
- else:
- exp = Decimal(u'1.0') / (Decimal(10) ** abs(p))
- try:
- # Set the precision high enough to avoid an exception, see #15789.
- tupl = d.as_tuple()
- units = len(tupl[1]) - tupl[2]
- prec = abs(p) + units + 1
- # Avoid conversion to scientific notation by accessing `sign`, `digits`
- # and `exponent` from `Decimal.as_tuple()` directly.
- sign, digits, exponent = d.quantize(exp, ROUND_HALF_UP,
- Context(prec=prec)).as_tuple()
- digits = [unicode(digit) for digit in reversed(digits)]
- while len(digits) <= abs(exponent):
- digits.append(u'0')
- digits.insert(-exponent, u'.')
- if sign:
- digits.append(u'-')
- number = u''.join(reversed(digits))
- return mark_safe(formats.number_format(number, abs(p)))
- except InvalidOperation:
- return input_val
- @register.filter(is_safe=True)
- @stringfilter
- def iriencode(value):
- """Escapes an IRI value for use in a URL."""
- return force_unicode(iri_to_uri(value))
- @register.filter(is_safe=True, needs_autoescape=True)
- @stringfilter
- def linenumbers(value, autoescape=None):
- """Displays text with line numbers."""
- lines = value.split(u'\n')
- # Find the maximum width of the line count, for use with zero padding
- # string format command
- width = unicode(len(unicode(len(lines))))
- if not autoescape or isinstance(value, SafeData):
- for i, line in enumerate(lines):
- lines[i] = (u"%0" + width + u"d. %s") % (i + 1, line)
- else:
- for i, line in enumerate(lines):
- lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line))
- return mark_safe(u'\n'.join(lines))
- @register.filter(is_safe=True)
- @stringfilter
- def lower(value):
- """Converts a string into all lowercase."""
- return value.lower()
- @register.filter(is_safe=False)
- @stringfilter
- def make_list(value):
- """
- Returns the value turned into a list.
- For an integer, it's a list of digits.
- For a string, it's a list of characters.
- """
- return list(value)
- @register.filter(is_safe=True)
- @stringfilter
- def slugify(value):
- """
- Normalizes string, converts to lowercase, removes non-alpha characters,
- and converts spaces to hyphens.
- """
- value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
- value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
- return mark_safe(re.sub('[-\s]+', '-', value))
- @register.filter(is_safe=True)
- def stringformat(value, arg):
- """
- Formats the variable according to the arg, a string formatting specifier.
- This specifier uses Python string formating syntax, with the exception that
- the leading "%" is dropped.
- See http://docs.python.org/lib/typesseq-strings.html for documentation
- of Python string formatting
- """
- try:
- return (u"%" + unicode(arg)) % value
- except (ValueError, TypeError):
- return u""
- @register.filter(is_safe=True)
- @stringfilter
- def title(value):
- """Converts a string into titlecase."""
- 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)
- @register.filter(is_safe=True)
- @stringfilter
- def truncatechars(value, arg):
- """
- Truncates a string after a certain number of characters.
- Argument: Number of characters to truncate after.
- """
- try:
- length = int(arg)
- except ValueError: # Invalid literal for int().
- return value # Fail silently.
- return Truncator(value).chars(length)
- @register.filter(is_safe=True)
- @stringfilter
- def truncatewords(value, arg):
- """
- Truncates a string after a certain number of words.
- Argument: Number of words to truncate after.
- Newlines within the string are removed.
- """
- try:
- length = int(arg)
- except ValueError: # Invalid literal for int().
- return value # Fail silently.
- return Truncator(value).words(length, truncate=' ...')
- @register.filter(is_safe=True)
- @stringfilter
- def truncatewords_html(value, arg):
- """
- Truncates HTML after a certain number of words.
- Argument: Number of words to truncate after.
- Newlines in the HTML are preserved.
- """
- try:
- length = int(arg)
- except ValueError: # invalid literal for int()
- return value # Fail silently.
- return Truncator(value).words(length, html=True, truncate=' ...')
- @register.filter(is_safe=False)
- @stringfilter
- def upper(value):
- """Converts a string into all uppercase."""
- return value.upper()
- @register.filter(is_safe=False)
- @stringfilter
- def urlencode(value, safe=None):
- """
- Escapes a value for use in a URL.
- Takes an optional ``safe`` parameter used to determine the characters which
- should not be escaped by Django's ``urlquote`` method. If not provided, the
- default safe characters will be used (but an empty string can be provided
- when *all* characters should be escaped).
- """
- kwargs = {}
- if safe is not None:
- kwargs['safe'] = safe
- return urlquote(value, **kwargs)
- @register.filter(is_safe=True, needs_autoescape=True)
- @stringfilter
- def urlize(value, autoescape=None):
- """Converts URLs in plain text into clickable links."""
- return mark_safe(urlize_impl(value, nofollow=True, autoescape=autoescape))
- @register.filter(is_safe=True, needs_autoescape=True)
- @stringfilter
- def urlizetrunc(value, limit, autoescape=None):
- """
- Converts URLs into clickable links, truncating URLs to the given character
- limit, and adding 'rel=nofollow' attribute to discourage spamming.
- Argument: Length to truncate URLs to.
- """
- return mark_safe(urlize_impl(value, trim_url_limit=int(limit), nofollow=True,
- autoescape=autoescape))
- @register.filter(is_safe=False)
- @stringfilter
- def wordcount(value):
- """Returns the number of words."""
- return len(value.split())
- @register.filter(is_safe=True)
- @stringfilter
- def wordwrap(value, arg):
- """
- Wraps words at specified line length.
- Argument: number of characters to wrap the text at.
- """
- return wrap(value, int(arg))
- @register.filter(is_safe=True)
- @stringfilter
- def ljust(value, arg):
- """
- Left-aligns the value in a field of a given width.
- Argument: field size.
- """
- return value.ljust(int(arg))
- @register.filter(is_safe=True)
- @stringfilter
- def rjust(value, arg):
- """
- Right-aligns the value in a field of a given width.
- Argument: field size.
- """
- return value.rjust(int(arg))
- @register.filter(is_safe=True)
- @stringfilter
- def center(value, arg):
- """Centers the value in a field of a given width."""
- return value.center(int(arg))
- @register.filter
- @stringfilter
- def cut(value, arg):
- """
- Removes all values of arg from the given string.
- """
- safe = isinstance(value, SafeData)
- value = value.replace(arg, u'')
- if safe and arg != ';':
- return mark_safe(value)
- return value
- ###################
- # HTML STRINGS #
- ###################
- @register.filter("escape", is_safe=True)
- @stringfilter
- def escape_filter(value):
- """
- Marks the value as a string that should not be auto-escaped.
- """
- return mark_for_escaping(value)
- @register.filter(is_safe=True)
- @stringfilter
- def force_escape(value):
- """
- Escapes a string's HTML. This returns a new string containing the escaped
- characters (as opposed to "escape", which marks the content for later
- possible escaping).
- """
- return mark_safe(escape(value))
- @register.filter("linebreaks", is_safe=True, needs_autoescape=True)
- @stringfilter
- def linebreaks_filter(value, autoescape=None):
- """
- Replaces line breaks in plain text with appropriate HTML; a single
- newline becomes an HTML line break (``<br />``) and a new line
- followed by a blank line becomes a paragraph break (``</p>``).
- """
- autoescape = autoescape and not isinstance(value, SafeData)
- return mark_safe(linebreaks(value, autoescape))
- @register.filter(is_safe=True, needs_autoescape=True)
- @stringfilter
- def linebreaksbr(value, autoescape=None):
- """
- Converts all newlines in a piece of plain text to HTML line breaks
- (``<br />``).
- """
- autoescape = autoescape and not isinstance(value, SafeData)
- value = normalize_newlines(value)
- if autoescape:
- value = escape(value)
- return mark_safe(value.replace('\n', '<br />'))
- @register.filter(is_safe=True)
- @stringfilter
- def safe(value):
- """
- Marks the value as a string that should not be auto-escaped.
- """
- return mark_safe(value)
- @register.filter(is_safe=True)
- def safeseq(value):
- """
- A "safe" filter for sequences. Marks each element in the sequence,
- individually, as safe, after converting them to unicode. Returns a list
- with the results.
- """
- return [mark_safe(force_unicode(obj)) for obj in value]
- @register.filter(is_safe=True)
- @stringfilter
- def removetags(value, tags):
- """Removes a space separated list of [X]HTML tags from the output."""
- tags = [re.escape(tag) for tag in tags.split()]
- tags_re = u'(%s)' % u'|'.join(tags)
- starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U)
- endtag_re = re.compile(u'</%s>' % tags_re)
- value = starttag_re.sub(u'', value)
- value = endtag_re.sub(u'', value)
- return value
- @register.filter(is_safe=True)
- @stringfilter
- def striptags(value):
- """Strips all [X]HTML tags."""
- return strip_tags(value)
- ###################
- # LISTS #
- ###################
- @register.filter(is_safe=False)
- def dictsort(value, arg):
- """
- Takes a list of dicts, returns that list sorted by the property given in
- the argument.
- """
- try:
- return sorted(value, key=Variable(arg).resolve)
- except (TypeError, VariableDoesNotExist):
- return u''
- @register.filter(is_safe=False)
- def dictsortreversed(value, arg):
- """
- Takes a list of dicts, returns that list sorted in reverse order by the
- property given in the argument.
- """
- try:
- return sorted(value, key=Variable(arg).resolve, reverse=True)
- except (TypeError, VariableDoesNotExist):
- return u''
- @register.filter(is_safe=False)
- def first(value):
- """Returns the first item in a list."""
- try:
- return value[0]
- except IndexError:
- return u''
- @register.filter(is_safe=True, needs_autoescape=True)
- def join(value, arg, autoescape=None):
- """
- Joins a list with a string, like Python's ``str.join(list)``.
- """
- value = map(force_unicode, value)
- if autoescape:
- value = [conditional_escape(v) for v in value]
- try:
- data = conditional_escape(arg).join(value)
- except AttributeError: # fail silently but nicely
- return value
- return mark_safe(data)
- @register.filter(is_safe=True)
- def last(value):
- "Returns the last item in a list"
- try:
- return value[-1]
- except IndexError:
- return u''
- @register.filter(is_safe=True)
- def length(value):
- """Returns the length of the value - useful for lists."""
- try:
- return len(value)
- except (ValueError, TypeError):
- return ''
- @register.filter(is_safe=False)
- def length_is(value, arg):
- """Returns a boolean of whether the value's length is the argument."""
- try:
- return len(value) == int(arg)
- except (ValueError, TypeError):
- return ''
- @register.filter(is_safe=True)
- def random(value):
- """Returns a random item from the list."""
- return random_module.choice(value)
- @register.filter("slice", is_safe=True)
- def slice_filter(value, arg):
- """
- Returns a slice of the list.
- Uses the same syntax as Python's list slicing; see
- http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
- for an introduction.
- """
- try:
- bits = []
- for x in arg.split(u':'):
- if len(x) == 0:
- bits.append(None)
- else:
- bits.append(int(x))
- return value[slice(*bits)]
- except (ValueError, TypeError):
- return value # Fail silently.
- @register.filter(is_safe=True, needs_autoescape=True)
- def unordered_list(value, autoescape=None):
- """
- Recursively takes a self-nested list and returns an HTML unordered list --
- WITHOUT opening and closing <ul> tags.
- The list is assumed to be in the proper format. For example, if ``var``
- contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``,
- then ``{{ var|unordered_list }}`` would return::
- <li>States
- <ul>
- <li>Kansas
- <ul>
- <li>Lawrence</li>
- <li>Topeka</li>
- </ul>
- </li>
- <li>Illinois</li>
- </ul>
- </li>
- """
- if autoescape:
- escaper = conditional_escape
- else:
- escaper = lambda x: x
- def convert_old_style_list(list_):
- """
- Converts old style lists to the new easier to understand format.
- The old list format looked like:
- ['Item 1', [['Item 1.1', []], ['Item 1.2', []]]
- And it is converted to:
- ['Item 1', ['Item 1.1', 'Item 1.2]]
- """
- if not isinstance(list_, (tuple, list)) or len(list_) != 2:
- return list_, False
- first_item, second_item = list_
- if second_item == []:
- return [first_item], True
- try:
- # see if second item is iterable
- iter(second_item)
- except TypeError:
- return list_, False
- old_style_list = True
- new_second_item = []
- for sublist in second_item:
- item, old_style_list = convert_old_style_list(sublist)
- if not old_style_list:
- break
- new_second_item.extend(item)
- if old_style_list:
- second_item = new_second_item
- return [first_item, second_item], old_style_list
- def _helper(list_, tabs=1):
- indent = u'\t' * tabs
- output = []
- list_length = len(list_)
- i = 0
- while i < list_length:
- title = list_[i]
- sublist = ''
- sublist_item = None
- if isinstance(title, (list, tuple)):
- sublist_item = title
- title = ''
- elif i < list_length - 1:
- next_item = list_[i+1]
- if next_item and isinstance(next_item, (list, tuple)):
- # The next item is a sub-list.
- sublist_item = next_item
- # We've processed the next item now too.
- i += 1
- if sublist_item:
- sublist = _helper(sublist_item, tabs+1)
- sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist,
- indent, indent)
- output.append('%s<li>%s%s</li>' % (indent,
- escaper(force_unicode(title)), sublist))
- i += 1
- return '\n'.join(output)
- value, converted = convert_old_style_list(value)
- return mark_safe(_helper(value))
- ###################
- # INTEGERS #
- ###################
- @register.filter(is_safe=False)
- def add(value, arg):
- """Adds the arg to the value."""
- try:
- return int(value) + int(arg)
- except (ValueError, TypeError):
- try:
- return value + arg
- except Exception:
- return ''
- @register.filter(is_safe=False)
- def get_digit(value, arg):
- """
- Given a whole number, returns the requested digit of it, where 1 is the
- right-most digit, 2 is the second-right-most digit, etc. Returns the
- original value for invalid input (if input or argument is not an integer,
- or if argument is less than 1). Otherwise, output is always an integer.
- """
- try:
- arg = int(arg)
- value = int(value)
- except ValueError:
- return value # Fail silently for an invalid argument
- if arg < 1:
- return value
- try:
- return int(str(value)[-arg])
- except IndexError:
- return 0
- ###################
- # DATES #
- ###################
- @register.filter(expects_localtime=True, is_safe=False)
- def date(value, arg=None):
- """Formats a date according to the given format."""
- if not value:
- return u''
- if arg is None:
- arg = settings.DATE_FORMAT
- try:
- return formats.date_format(value, arg)
- except AttributeError:
- try:
- return format(value, arg)
- except AttributeError:
- return ''
- @register.filter(expects_localtime=True, is_safe=False)
- def time(value, arg=None):
- """Formats a time according to the given format."""
- if value in (None, u''):
- return u''
- if arg is None:
- arg = settings.TIME_FORMAT
- try:
- return formats.time_format(value, arg)
- except AttributeError:
- try:
- return time_format(value, arg)
- except AttributeError:
- return ''
- @register.filter("timesince", is_safe=False)
- def timesince_filter(value, arg=None):
- """Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
- if not value:
- return u''
- try:
- if arg:
- return timesince(value, arg)
- return timesince(value)
- except (ValueError, TypeError):
- return u''
- @register.filter("timeuntil", is_safe=False)
- def timeuntil_filter(value, arg=None):
- """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
- if not value:
- return u''
- try:
- return timeuntil(value, arg)
- except (ValueError, TypeError):
- return u''
- ###################
- # LOGIC #
- ###################
- @register.filter(is_safe=False)
- def default(value, arg):
- """If value is unavailable, use given default."""
- return value or arg
- @register.filter(is_safe=False)
- def default_if_none(value, arg):
- """If value is None, use given default."""
- if value is None:
- return arg
- return value
- @register.filter(is_safe=False)
- def divisibleby(value, arg):
- """Returns True if the value is devisible by the argument."""
- return int(value) % int(arg) == 0
- @register.filter(is_safe=False)
- def yesno(value, arg=None):
- """
- Given a string mapping values for true, false and (optionally) None,
- returns one of those strings according to the value:
- ========== ====================== ==================================
- Value Argument Outputs
- ========== ====================== ==================================
- ``True`` ``"yeah,no,maybe"`` ``yeah``
- ``False`` ``"yeah,no,maybe"`` ``no``
- ``None`` ``"yeah,no,maybe"`` ``maybe``
- ``None`` ``"yeah,no"`` ``"no"`` (converts None to False
- if no mapping for None is given.
- ========== ====================== ==================================
- """
- if arg is None:
- arg = ugettext('yes,no,maybe')
- bits = arg.split(u',')
- if len(bits) < 2:
- return value # Invalid arg.
- try:
- yes, no, maybe = bits
- except ValueError:
- # Unpack list of wrong size (no "maybe" value provided).
- yes, no, maybe = bits[0], bits[1], bits[1]
- if value is None:
- return maybe
- if value:
- return yes
- return no
- ###################
- # MISC #
- ###################
- @register.filter(is_safe=True)
- def filesizeformat(bytes):
- """
- Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
- 102 bytes, etc).
- """
- try:
- bytes = float(bytes)
- except (TypeError,ValueError,UnicodeDecodeError):
- return ungettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
- filesize_number_format = lambda value: formats.number_format(round(value, 1), 1)
- if bytes < 1024:
- return ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
- if bytes < 1024 * 1024:
- return ugettext("%s KB") % filesize_number_format(bytes / 1024)
- if bytes < 1024 * 1024 * 1024:
- return ugettext("%s MB") % filesize_number_format(bytes / (1024 * 1024))
- if bytes < 1024 * 1024 * 1024 * 1024:
- return ugettext("%s GB") % filesize_number_format(bytes / (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 PB") % filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024 * 1024))
- @register.filter(is_safe=False)
- def pluralize(value, arg=u's'):
- """
- Returns a plural suffix if the value is not 1. By default, 's' is used as
- the suffix:
- * If value is 0, vote{{ value|pluralize }} displays "0 votes".
- * If value is 1, vote{{ value|pluralize }} displays "1 vote".
- * If value is 2, vote{{ value|pluralize }} displays "2 votes".
- If an argument is provided, that string is used instead:
- * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes".
- * If value is 1, class{{ value|pluralize:"es" }} displays "1 class".
- * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes".
- If the provided argument contains a comma, the text before the comma is
- used for the singular case and the text after the comma is used for the
- plural case:
- * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies".
- * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy".
- * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies".
- """
- if not u',' in arg:
- arg = u',' + arg
- bits = arg.split(u',')
- if len(bits) > 2:
- return u''
- singular_suffix, plural_suffix = bits[:2]
- try:
- if int(value) != 1:
- return plural_suffix
- except ValueError: # Invalid string that's not a number.
- pass
- except TypeError: # Value isn't a string or a number; maybe it's a list?
- try:
- if len(value) != 1:
- return plural_suffix
- except TypeError: # len() of unsized object.
- pass
- return singular_suffix
- @register.filter("phone2numeric", is_safe=True)
- def phone2numeric_filter(value):
- """Takes a phone number and converts it in to its numerical equivalent."""
- return phone2numeric(value)
- @register.filter(is_safe=True)
- def pprint(value):
- """A wrapper around pprint.pprint -- for debugging, really."""
- try:
- return pformat(value)
- except Exception, e:
- return u"Error in formatting: %s" % force_unicode(e, errors="replace")