Ticket #626: 626_import_error.patch
File 626_import_error.patch, 64.7 KB (added by , 19 years ago) |
---|
-
django/core/template_loader.py
1 1 "Wrapper for loading templates from storage of some sort (e.g. files or db)" 2 import template3 from template_file import load_template_source4 2 3 from django.core import template 4 from django.core.template.loaders.filesystem import load_template_source 5 5 6 class ExtendsError(Exception): 6 7 pass 7 8 … … 24 25 Loads the given template_name and renders it with the given dictionary as 25 26 context. The template_name may be a string to load a single template using 26 27 get_template, or it may be a tuple to use select_template to find one of 27 the templates in the list. Returns a string. 28 the templates in the list. Returns a string. 28 29 """ 29 30 dictionary = dictionary or {} 30 31 if isinstance(template_name, (list, tuple)): -
django/core/defaulttags.py
1 "Default tags used by the template system, available to all templates."2 3 import sys4 import template5 6 class CommentNode(template.Node):7 def render(self, context):8 return ''9 10 class CycleNode(template.Node):11 def __init__(self, cyclevars):12 self.cyclevars = cyclevars13 self.cyclevars_len = len(cyclevars)14 self.counter = -115 16 def render(self, context):17 self.counter += 118 return self.cyclevars[self.counter % self.cyclevars_len]19 20 class DebugNode(template.Node):21 def render(self, context):22 from pprint import pformat23 output = [pformat(val) for val in context]24 output.append('\n\n')25 output.append(pformat(sys.modules))26 return ''.join(output)27 28 class FilterNode(template.Node):29 def __init__(self, filters, nodelist):30 self.filters, self.nodelist = filters, nodelist31 32 def render(self, context):33 output = self.nodelist.render(context)34 # apply filters35 for f in self.filters:36 output = template.registered_filters[f[0]][0](output, f[1])37 return output38 39 class FirstOfNode(template.Node):40 def __init__(self, vars):41 self.vars = vars42 43 def render(self, context):44 for var in self.vars:45 value = template.resolve_variable(var, context)46 if value:47 return str(value)48 return ''49 50 class ForNode(template.Node):51 def __init__(self, loopvar, sequence, reversed, nodelist_loop):52 self.loopvar, self.sequence = loopvar, sequence53 self.reversed = reversed54 self.nodelist_loop = nodelist_loop55 56 def __repr__(self):57 if self.reversed:58 reversed = ' reversed'59 else:60 reversed = ''61 return "<For Node: for %s in %s, tail_len: %d%s>" % \62 (self.loopvar, self.sequence, len(self.nodelist_loop), reversed)63 64 def __iter__(self):65 for node in self.nodelist_loop:66 yield node67 68 def get_nodes_by_type(self, nodetype):69 nodes = []70 if isinstance(self, nodetype):71 nodes.append(self)72 nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))73 return nodes74 75 def render(self, context):76 nodelist = template.NodeList()77 if context.has_key('forloop'):78 parentloop = context['forloop']79 else:80 parentloop = {}81 context.push()82 try:83 values = template.resolve_variable_with_filters(self.sequence, context)84 except template.VariableDoesNotExist:85 values = []86 if values is None:87 values = []88 len_values = len(values)89 if self.reversed:90 # From http://www.python.org/doc/current/tut/node11.html91 def reverse(data):92 for index in range(len(data)-1, -1, -1):93 yield data[index]94 values = reverse(values)95 for i, item in enumerate(values):96 context['forloop'] = {97 # shortcuts for current loop iteration number98 'counter0': i,99 'counter': i+1,100 # reverse counter iteration numbers101 'revcounter': len_values - i,102 'revcounter0': len_values - i - 1,103 # boolean values designating first and last times through loop104 'first': (i == 0),105 'last': (i == len_values - 1),106 'parentloop': parentloop,107 }108 context[self.loopvar] = item109 for node in self.nodelist_loop:110 nodelist.append(node.render(context))111 context.pop()112 return nodelist.render(context)113 114 class IfChangedNode(template.Node):115 def __init__(self, nodelist):116 self.nodelist = nodelist117 self._last_seen = None118 119 def render(self, context):120 content = self.nodelist.render(context)121 if content != self._last_seen:122 firstloop = (self._last_seen == None)123 self._last_seen = content124 context.push()125 context['ifchanged'] = {'firstloop': firstloop}126 content = self.nodelist.render(context)127 context.pop()128 return content129 else:130 return ''131 132 class IfEqualNode(template.Node):133 def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):134 self.var1, self.var2 = var1, var2135 self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false136 self.negate = negate137 138 def __repr__(self):139 return "<IfEqualNode>"140 141 def render(self, context):142 val1 = template.resolve_variable(self.var1, context)143 val2 = template.resolve_variable(self.var2, context)144 if (self.negate and val1 != val2) or (not self.negate and val1 == val2):145 return self.nodelist_true.render(context)146 return self.nodelist_false.render(context)147 148 class IfNode(template.Node):149 def __init__(self, boolvars, nodelist_true, nodelist_false):150 self.boolvars = boolvars151 self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false152 153 def __repr__(self):154 return "<If node>"155 156 def __iter__(self):157 for node in self.nodelist_true:158 yield node159 for node in self.nodelist_false:160 yield node161 162 def get_nodes_by_type(self, nodetype):163 nodes = []164 if isinstance(self, nodetype):165 nodes.append(self)166 nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))167 nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))168 return nodes169 170 def render(self, context):171 for ifnot, boolvar in self.boolvars:172 try:173 value = template.resolve_variable_with_filters(boolvar, context)174 except template.VariableDoesNotExist:175 value = None176 if (value and not ifnot) or (ifnot and not value):177 return self.nodelist_true.render(context)178 return self.nodelist_false.render(context)179 180 class RegroupNode(template.Node):181 def __init__(self, target_var, expression, var_name):182 self.target_var, self.expression = target_var, expression183 self.var_name = var_name184 185 def render(self, context):186 obj_list = template.resolve_variable_with_filters(self.target_var, context)187 if obj_list == '': # target_var wasn't found in context; fail silently188 context[self.var_name] = []189 return ''190 output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}191 for obj in obj_list:192 grouper = template.resolve_variable_with_filters('var.%s' % self.expression, \193 template.Context({'var': obj}))194 if output and repr(output[-1]['grouper']) == repr(grouper):195 output[-1]['list'].append(obj)196 else:197 output.append({'grouper': grouper, 'list': [obj]})198 context[self.var_name] = output199 return ''200 201 def include_is_allowed(filepath):202 from django.conf.settings import ALLOWED_INCLUDE_ROOTS203 for root in ALLOWED_INCLUDE_ROOTS:204 if filepath.startswith(root):205 return True206 return False207 208 class SsiNode(template.Node):209 def __init__(self, filepath, parsed):210 self.filepath, self.parsed = filepath, parsed211 212 def render(self, context):213 if not include_is_allowed(self.filepath):214 return '' # Fail silently for invalid includes.215 try:216 fp = open(self.filepath, 'r')217 output = fp.read()218 fp.close()219 except IOError:220 output = ''221 if self.parsed:222 try:223 t = template.Template(output)224 return t.render(context)225 except template.TemplateSyntaxError:226 return '' # Fail silently for invalid included templates.227 return output228 229 class LoadNode(template.Node):230 def __init__(self, taglib):231 self.taglib = taglib232 233 def load_taglib(taglib):234 mod = __import__("django.templatetags.%s" % taglib.split('.')[-1], '', '', [''])235 reload(mod)236 return mod237 load_taglib = staticmethod(load_taglib)238 239 def render(self, context):240 "Import the relevant module"241 try:242 self.__class__.load_taglib(self.taglib)243 except ImportError:244 pass # Fail silently for invalid loads.245 return ''246 247 class NowNode(template.Node):248 def __init__(self, format_string):249 self.format_string = format_string250 251 def render(self, context):252 from datetime import datetime253 from django.utils.dateformat import DateFormat254 df = DateFormat(datetime.now())255 return df.format(self.format_string)256 257 class TemplateTagNode(template.Node):258 mapping = {'openblock': template.BLOCK_TAG_START,259 'closeblock': template.BLOCK_TAG_END,260 'openvariable': template.VARIABLE_TAG_START,261 'closevariable': template.VARIABLE_TAG_END}262 263 def __init__(self, tagtype):264 self.tagtype = tagtype265 266 def render(self, context):267 return self.mapping.get(self.tagtype, '')268 269 class WidthRatioNode(template.Node):270 def __init__(self, val_var, max_var, max_width):271 self.val_var = val_var272 self.max_var = max_var273 self.max_width = max_width274 275 def render(self, context):276 try:277 value = template.resolve_variable_with_filters(self.val_var, context)278 maxvalue = template.resolve_variable_with_filters(self.max_var, context)279 except template.VariableDoesNotExist:280 return ''281 try:282 value = float(value)283 maxvalue = float(maxvalue)284 ratio = (value / maxvalue) * int(self.max_width)285 except (ValueError, ZeroDivisionError):286 return ''287 return str(int(round(ratio)))288 289 def do_comment(parser, token):290 """291 Ignore everything between ``{% comment %}`` and ``{% endcomment %}``292 """293 nodelist = parser.parse(('endcomment',))294 parser.delete_first_token()295 return CommentNode()296 297 def do_cycle(parser, token):298 """299 Cycle among the given strings each time this tag is encountered300 301 Within a loop, cycles among the given strings each time through302 the loop::303 304 {% for o in some_list %}305 <tr class="{% cycle row1,row2 %}">306 ...307 </tr>308 {% endfor %}309 310 Outside of a loop, give the values a unique name the first time you call311 it, then use that name each sucessive time through::312 313 <tr class="{% cycle row1,row2,row3 as rowcolors %}">...</tr>314 <tr class="{% cycle rowcolors %}">...</tr>315 <tr class="{% cycle rowcolors %}">...</tr>316 317 You can use any number of values, seperated by commas. Make sure not to318 put spaces between the values -- only commas.319 """320 321 # Note: This returns the exact same node on each {% cycle name %} call; that322 # is, the node object returned from {% cycle a,b,c as name %} and the one323 # returned from {% cycle name %} are the exact same object. This shouldn't324 # cause problems (heh), but if it does, now you know.325 #326 # Ugly hack warning: this stuffs the named template dict into parser so327 # that names are only unique within each template (as opposed to using328 # a global variable, which would make cycle names have to be unique across329 # *all* templates.330 331 args = token.contents.split()332 if len(args) < 2:333 raise template.TemplateSyntaxError("'Cycle' statement requires at least two arguments")334 335 elif len(args) == 2 and "," in args[1]:336 # {% cycle a,b,c %}337 cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks338 return CycleNode(cyclevars)339 # {% cycle name %}340 341 elif len(args) == 2:342 name = args[1]343 if not parser._namedCycleNodes.has_key(name):344 raise template.TemplateSyntaxError("Named cycle '%s' does not exist" % name)345 return parser._namedCycleNodes[name]346 347 elif len(args) == 4:348 # {% cycle a,b,c as name %}349 if args[2] != 'as':350 raise template.TemplateSyntaxError("Second 'cycle' argument must be 'as'")351 cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks352 name = args[3]353 node = CycleNode(cyclevars)354 355 if not hasattr(parser, '_namedCycleNodes'):356 parser._namedCycleNodes = {}357 358 parser._namedCycleNodes[name] = node359 return node360 361 else:362 raise template.TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args)363 364 def do_debug(parser, token):365 "Print a whole load of debugging information, including the context and imported modules"366 return DebugNode()367 368 def do_filter(parser, token):369 """370 Filter the contents of the blog through variable filters.371 372 Filters can also be piped through each other, and they can have373 arguments -- just like in variable syntax.374 375 Sample usage::376 377 {% filter escape|lower %}378 This text will be HTML-escaped, and will appear in lowercase.379 {% endfilter %}380 """381 _, rest = token.contents.split(None, 1)382 _, filters = template.get_filters_from_token('var|%s' % rest)383 nodelist = parser.parse(('endfilter',))384 parser.delete_first_token()385 return FilterNode(filters, nodelist)386 387 def do_firstof(parser, token):388 """389 Outputs the first variable passed that is not False.390 391 Outputs nothing if all the passed variables are False.392 393 Sample usage::394 395 {% firstof var1 var2 var3 %}396 397 This is equivalent to::398 399 {% if var1 %}400 {{ var1 }}401 {% else %}{% if var2 %}402 {{ var2 }}403 {% else %}{% if var3 %}404 {{ var3 }}405 {% endif %}{% endif %}{% endif %}406 407 but obviously much cleaner!408 """409 bits = token.contents.split()[1:]410 if len(bits) < 1:411 raise template.TemplateSyntaxError, "'firstof' statement requires at least one argument"412 return FirstOfNode(bits)413 414 415 def do_for(parser, token):416 """417 Loop over each item in an array.418 419 For example, to display a list of athletes given ``athlete_list``::420 421 <ul>422 {% for athlete in athlete_list %}423 <li>{{ athlete.name }}</li>424 {% endfor %}425 </ul>426 427 You can also loop over a list in reverse by using428 ``{% for obj in list reversed %}``.429 430 The for loop sets a number of variables available within the loop:431 432 ========================== ================================================433 Variable Description434 ========================== ================================================435 ``forloop.counter`` The current iteration of the loop (1-indexed)436 ``forloop.counter0`` The current iteration of the loop (0-indexed)437 ``forloop.revcounter`` The number of iterations from the end of the438 loop (1-indexed)439 ``forloop.revcounter0`` The number of iterations from the end of the440 loop (0-indexed)441 ``forloop.first`` True if this is the first time through the loop442 ``forloop.last`` True if this is the last time through the loop443 ``forloop.parentloop`` For nested loops, this is the loop "above" the444 current one445 ========================== ================================================446 447 """448 bits = token.contents.split()449 if len(bits) == 5 and bits[4] != 'reversed':450 raise template.TemplateSyntaxError, "'for' statements with five words should end in 'reversed': %s" % token.contents451 if len(bits) not in (4, 5):452 raise template.TemplateSyntaxError, "'for' statements should have either four or five words: %s" % token.contents453 if bits[2] != 'in':454 raise template.TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents455 loopvar = bits[1]456 sequence = bits[3]457 reversed = (len(bits) == 5)458 nodelist_loop = parser.parse(('endfor',))459 parser.delete_first_token()460 return ForNode(loopvar, sequence, reversed, nodelist_loop)461 462 def do_ifequal(parser, token, negate):463 """464 Output the contents of the block if the two arguments equal/don't equal each other.465 466 Examples::467 468 {% ifequal user.id comment.user_id %}469 ...470 {% endifequal %}471 472 {% ifnotequal user.id comment.user_id %}473 ...474 {% else %}475 ...476 {% endifnotequal %}477 """478 bits = token.contents.split()479 if len(bits) != 3:480 raise template.TemplateSyntaxError, "%r takes two arguments" % bits[0]481 end_tag = 'end' + bits[0]482 nodelist_true = parser.parse(('else', end_tag))483 token = parser.next_token()484 if token.contents == 'else':485 nodelist_false = parser.parse((end_tag,))486 parser.delete_first_token()487 else:488 nodelist_false = template.NodeList()489 return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate)490 491 def do_if(parser, token):492 """493 The ``{% if %}`` tag evaluates a variable, and if that variable is "true"494 (i.e. exists, is not empty, and is not a false boolean value) the contents495 of the block are output:496 497 ::498 499 {% if althlete_list %}500 Number of athletes: {{ althete_list|count }}501 {% else %}502 No athletes.503 {% endif %}504 505 In the above, if ``athlete_list`` is not empty, the number of athletes will506 be displayed by the ``{{ athlete_list|count }}`` variable.507 508 As you can see, the ``if`` tag can take an option ``{% else %}`` clause that509 will be displayed if the test fails.510 511 ``if`` tags may use ``or`` or ``not`` to test a number of variables or to512 negate a given variable::513 514 {% if not athlete_list %}515 There are no athletes.516 {% endif %}517 518 {% if athlete_list or coach_list %}519 There are some athletes or some coaches.520 {% endif %}521 522 {% if not athlete_list or coach_list %}523 There are no athletes or there are some coaches (OK, so524 writing English translations of boolean logic sounds525 stupid; it's not my fault).526 {% endif %}527 528 For simplicity, ``if`` tags do not allow ``and`` clauses; use nested ``if``529 tags instead::530 531 {% if athlete_list %}532 {% if coach_list %}533 Number of athletes: {{ athlete_list|count }}.534 Number of coaches: {{ coach_list|count }}.535 {% endif %}536 {% endif %}537 """538 bits = token.contents.split()539 del bits[0]540 if not bits:541 raise template.TemplateSyntaxError, "'if' statement requires at least one argument"542 # bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d']543 boolpairs = ' '.join(bits).split(' or ')544 boolvars = []545 for boolpair in boolpairs:546 if ' ' in boolpair:547 not_, boolvar = boolpair.split()548 if not_ != 'not':549 raise template.TemplateSyntaxError, "Expected 'not' in if statement"550 boolvars.append((True, boolvar))551 else:552 boolvars.append((False, boolpair))553 nodelist_true = parser.parse(('else', 'endif'))554 token = parser.next_token()555 if token.contents == 'else':556 nodelist_false = parser.parse(('endif',))557 parser.delete_first_token()558 else:559 nodelist_false = template.NodeList()560 return IfNode(boolvars, nodelist_true, nodelist_false)561 562 def do_ifchanged(parser, token):563 """564 Check if a value has changed from the last iteration of a loop.565 566 The 'ifchanged' block tag is used within a loop. It checks its own rendered567 contents against its previous state and only displays its content if the568 value has changed::569 570 <h1>Archive for {{ year }}</h1>571 572 {% for date in days %}573 {% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}574 <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>575 {% endfor %}576 """577 bits = token.contents.split()578 if len(bits) != 1:579 raise template.TemplateSyntaxError, "'ifchanged' tag takes no arguments"580 nodelist = parser.parse(('endifchanged',))581 parser.delete_first_token()582 return IfChangedNode(nodelist)583 584 def do_ssi(parser, token):585 """586 Output the contents of a given file into the page.587 588 Like a simple "include" tag, the ``ssi`` tag includes the contents589 of another file -- which must be specified using an absolute page --590 in the current page::591 592 {% ssi /home/html/ljworld.com/includes/right_generic.html %}593 594 If the optional "parsed" parameter is given, the contents of the included595 file are evaluated as template code, with the current context::596 597 {% ssi /home/html/ljworld.com/includes/right_generic.html parsed %}598 """599 bits = token.contents.split()600 parsed = False601 if len(bits) not in (2, 3):602 raise template.TemplateSyntaxError, "'ssi' tag takes one argument: the path to the file to be included"603 if len(bits) == 3:604 if bits[2] == 'parsed':605 parsed = True606 else:607 raise template.TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0]608 return SsiNode(bits[1], parsed)609 610 def do_load(parser, token):611 """612 Load a custom template tag set.613 614 For example, to load the template tags in ``django/templatetags/news/photos.py``::615 616 {% load news.photos %}617 """618 bits = token.contents.split()619 if len(bits) != 2:620 raise template.TemplateSyntaxError, "'load' statement takes one argument"621 taglib = bits[1]622 # check at compile time that the module can be imported623 try:624 LoadNode.load_taglib(taglib)625 except ImportError:626 raise template.TemplateSyntaxError, "'%s' is not a valid tag library" % taglib627 return LoadNode(taglib)628 629 def do_now(parser, token):630 """631 Display the date, formatted according to the given string.632 633 Uses the same format as PHP's ``date()`` function; see http://php.net/date634 for all the possible values.635 636 Sample usage::637 638 It is {% now "jS F Y H:i" %}639 """640 bits = token.contents.split('"')641 if len(bits) != 3:642 raise template.TemplateSyntaxError, "'now' statement takes one argument"643 format_string = bits[1]644 return NowNode(format_string)645 646 def do_regroup(parser, token):647 """648 Regroup a list of alike objects by a common attribute.649 650 This complex tag is best illustrated by use of an example: say that651 ``people`` is a list of ``Person`` objects that have ``first_name``,652 ``last_name``, and ``gender`` attributes, and you'd like to display a list653 that looks like:654 655 * Male:656 * George Bush657 * Bill Clinton658 * Female:659 * Margaret Thatcher660 * Colendeeza Rice661 * Unknown:662 * Pat Smith663 664 The following snippet of template code would accomplish this dubious task::665 666 {% regroup people by gender as grouped %}667 <ul>668 {% for group in grouped %}669 <li>{{ group.grouper }}670 <ul>671 {% for item in group.list %}672 <li>{{ item }}</li>673 {% endfor %}674 </ul>675 {% endfor %}676 </ul>677 678 As you can see, ``{% regroup %}`` populates a variable with a list of679 objects with ``grouper`` and ``list`` attributes. ``grouper`` contains the680 item that was grouped by; ``list`` contains the list of objects that share681 that ``grouper``. In this case, ``grouper`` would be ``Male``, ``Female``682 and ``Unknown``, and ``list`` is the list of people with those genders.683 684 Note that `{% regroup %}`` does not work when the list to be grouped is not685 sorted by the key you are grouping by! This means that if your list of686 people was not sorted by gender, you'd need to make sure it is sorted before687 using it, i.e.::688 689 {% regroup people|dictsort:"gender" by gender as grouped %}690 691 """692 firstbits = token.contents.split(None, 3)693 if len(firstbits) != 4:694 raise template.TemplateSyntaxError, "'regroup' tag takes five arguments"695 target_var = firstbits[1]696 if firstbits[2] != 'by':697 raise template.TemplateSyntaxError, "second argument to 'regroup' tag must be 'by'"698 lastbits_reversed = firstbits[3][::-1].split(None, 2)699 if lastbits_reversed[1][::-1] != 'as':700 raise template.TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'"701 expression = lastbits_reversed[2][::-1]702 var_name = lastbits_reversed[0][::-1]703 return RegroupNode(target_var, expression, var_name)704 705 def do_templatetag(parser, token):706 """707 Output one of the bits used to compose template tags.708 709 Since the template system has no concept of "escaping", to display one of710 the bits used in template tags, you must use the ``{% templatetag %}`` tag.711 712 The argument tells which template bit to output:713 714 ================== =======715 Argument Outputs716 ================== =======717 ``openblock`` ``{%``718 ``closeblock`` ``%}``719 ``openvariable`` ``{{``720 ``closevariable`` ``}}``721 ================== =======722 """723 bits = token.contents.split()724 if len(bits) != 2:725 raise template.TemplateSyntaxError, "'templatetag' statement takes one argument"726 tag = bits[1]727 if not TemplateTagNode.mapping.has_key(tag):728 raise template.TemplateSyntaxError, "Invalid templatetag argument: '%s'. Must be one of: %s" % \729 (tag, TemplateTagNode.mapping.keys())730 return TemplateTagNode(tag)731 732 def do_widthratio(parser, token):733 """734 For creating bar charts and such, this tag calculates the ratio of a given735 value to a maximum value, and then applies that ratio to a constant.736 737 For example::738 739 <img src='bar.gif' height='10' width='{% widthratio this_value max_value 100 %}' />740 741 Above, if ``this_value`` is 175 and ``max_value`` is 200, the the image in742 the above example will be 88 pixels wide (because 175/200 = .875; .875 *743 100 = 87.5 which is rounded up to 88).744 """745 bits = token.contents.split()746 if len(bits) != 4:747 raise template.TemplateSyntaxError("widthratio takes three arguments")748 tag, this_value_var, max_value_var, max_width = bits749 try:750 max_width = int(max_width)751 except ValueError:752 raise template.TemplateSyntaxError("widthratio final argument must be an integer")753 return WidthRatioNode(this_value_var, max_value_var, max_width)754 755 template.register_tag('comment', do_comment)756 template.register_tag('cycle', do_cycle)757 template.register_tag('debug', do_debug)758 template.register_tag('filter', do_filter)759 template.register_tag('firstof', do_firstof)760 template.register_tag('for', do_for)761 template.register_tag('ifequal', lambda parser, token: do_ifequal(parser, token, False))762 template.register_tag('ifnotequal', lambda parser, token: do_ifequal(parser, token, True))763 template.register_tag('if', do_if)764 template.register_tag('ifchanged', do_ifchanged)765 template.register_tag('regroup', do_regroup)766 template.register_tag('ssi', do_ssi)767 template.register_tag('load', do_load)768 template.register_tag('now', do_now)769 template.register_tag('templatetag', do_templatetag)770 template.register_tag('widthratio', do_widthratio) -
django/core/template_file.py
1 # Wrapper for loading templates from files2 3 from django.conf.settings import TEMPLATE_DIRS, TEMPLATE_FILE_EXTENSION4 from django.core.template import TemplateDoesNotExist5 import os6 7 def load_template_source(template_name, template_dirs=None):8 if not template_dirs:9 template_dirs = TEMPLATE_DIRS10 tried = []11 for template_dir in template_dirs:12 filepath = os.path.join(template_dir, template_name) + TEMPLATE_FILE_EXTENSION13 try:14 return open(filepath).read()15 except IOError:16 tried.append(filepath)17 if template_dirs:18 error_msg = "Tried %s" % tried19 else:20 error_msg = "Your TEMPLATE_DIRS settings is empty. Change it to point to at least one template directory."21 raise TemplateDoesNotExist, error_msg -
django/core/template.py
1 """2 This is the Django template system.3 4 How it works:5 6 The tokenize() function converts a template string (i.e., a string containing7 markup with custom template tags) to tokens, which can be either plain text8 (TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).9 10 The Parser() class takes a list of tokens in its constructor, and its parse()11 method returns a compiled template -- which is, under the hood, a list of12 Node objects.13 14 Each Node is responsible for creating some sort of output -- e.g. simple text15 (TextNode), variable values in a given context (VariableNode), results of basic16 logic (IfNode), results of looping (ForNode), or anything else. The core Node17 types are TextNode, VariableNode, IfNode and ForNode, but plugin modules can18 define their own custom node types.19 20 Each Node has a render() method, which takes a Context and returns a string of21 the rendered node. For example, the render() method of a Variable Node returns22 the variable's value as a string. The render() method of an IfNode returns the23 rendered output of whatever was inside the loop, recursively.24 25 The Template class is a convenient wrapper that takes care of template26 compilation and rendering.27 28 Usage:29 30 The only thing you should ever use directly in this file is the Template class.31 Create a compiled template object with a template_string, then call render()32 with a context. In the compilation stage, the TemplateSyntaxError exception33 will be raised if the template doesn't have proper syntax.34 35 Sample code:36 37 >>> import template38 >>> s = '''39 ... <html>40 ... {% if test %}41 ... <h1>{{ varvalue }}</h1>42 ... {% endif %}43 ... </html>44 ... '''45 >>> t = template.Template(s)46 47 (t is now a compiled template, and its render() method can be called multiple48 times with multiple contexts)49 50 >>> c = template.Context({'test':True, 'varvalue': 'Hello'})51 >>> t.render(c)52 '\n<html>\n\n <h1>Hello</h1>\n\n</html>\n'53 >>> c = template.Context({'test':False, 'varvalue': 'Hello'})54 >>> t.render(c)55 '\n<html>\n\n</html>\n'56 """57 import re58 from django.conf.settings import DEFAULT_CHARSET59 60 __all__ = ('Template','Context','compile_string')61 62 TOKEN_TEXT = 063 TOKEN_VAR = 164 TOKEN_BLOCK = 265 66 # template syntax constants67 FILTER_SEPARATOR = '|'68 FILTER_ARGUMENT_SEPARATOR = ':'69 VARIABLE_ATTRIBUTE_SEPARATOR = '.'70 BLOCK_TAG_START = '{%'71 BLOCK_TAG_END = '%}'72 VARIABLE_TAG_START = '{{'73 VARIABLE_TAG_END = '}}'74 75 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.'76 77 # match a variable or block tag and capture the entire tag, including start/end delimiters78 tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),79 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END)))80 81 # global dict used by register_tag; maps custom tags to callback functions82 registered_tags = {}83 84 # global dict used by register_filter; maps custom filters to callback functions85 registered_filters = {}86 87 class TemplateSyntaxError(Exception):88 pass89 90 class ContextPopException(Exception):91 "pop() has been called more times than push()"92 pass93 94 class TemplateDoesNotExist(Exception):95 pass96 97 class VariableDoesNotExist(Exception):98 pass99 100 class SilentVariableFailure(Exception):101 "Any function raising this exception will be ignored by resolve_variable"102 pass103 104 class Template:105 def __init__(self, template_string):106 "Compilation stage"107 self.nodelist = compile_string(template_string)108 109 def __iter__(self):110 for node in self.nodelist:111 for subnode in node:112 yield subnode113 114 def render(self, context):115 "Display stage -- can be called many times"116 return self.nodelist.render(context)117 118 def compile_string(template_string):119 "Compiles template_string into NodeList ready for rendering"120 tokens = tokenize(template_string)121 parser = Parser(tokens)122 return parser.parse()123 124 class Context:125 "A stack container for variable context"126 def __init__(self, dict=None):127 dict = dict or {}128 self.dicts = [dict]129 130 def __repr__(self):131 return repr(self.dicts)132 133 def __iter__(self):134 for d in self.dicts:135 yield d136 137 def push(self):138 self.dicts = [{}] + self.dicts139 140 def pop(self):141 if len(self.dicts) == 1:142 raise ContextPopException143 del self.dicts[0]144 145 def __setitem__(self, key, value):146 "Set a variable in the current context"147 self.dicts[0][key] = value148 149 def __getitem__(self, key):150 "Get a variable's value, starting at the current context and going upward"151 for dict in self.dicts:152 if dict.has_key(key):153 return dict[key]154 return ''155 156 def __delitem__(self, key):157 "Delete a variable from the current context"158 del self.dicts[0][key]159 160 def has_key(self, key):161 for dict in self.dicts:162 if dict.has_key(key):163 return True164 return False165 166 def update(self, other_dict):167 "Like dict.update(). Pushes an entire dictionary's keys and values onto the context."168 self.dicts = [other_dict] + self.dicts169 170 class Token:171 def __init__(self, token_type, contents):172 "The token_type must be TOKEN_TEXT, TOKEN_VAR or TOKEN_BLOCK"173 self.token_type, self.contents = token_type, contents174 175 def __str__(self):176 return '<%s token: "%s...">' % (177 {TOKEN_TEXT:'Text', TOKEN_VAR:'Var', TOKEN_BLOCK:'Block'}[self.token_type],178 self.contents[:20].replace('\n', '')179 )180 181 def tokenize(template_string):182 "Return a list of tokens from a given template_string"183 # remove all empty strings, because the regex has a tendency to add them184 bits = filter(None, tag_re.split(template_string))185 return map(create_token, bits)186 187 def create_token(token_string):188 "Convert the given token string into a new Token object and return it"189 if token_string.startswith(VARIABLE_TAG_START):190 return Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip())191 elif token_string.startswith(BLOCK_TAG_START):192 return Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip())193 else:194 return Token(TOKEN_TEXT, token_string)195 196 class Parser:197 def __init__(self, tokens):198 self.tokens = tokens199 200 def parse(self, parse_until=[]):201 nodelist = NodeList()202 while self.tokens:203 token = self.next_token()204 if token.token_type == TOKEN_TEXT:205 nodelist.append(TextNode(token.contents))206 elif token.token_type == TOKEN_VAR:207 if not token.contents:208 raise TemplateSyntaxError, "Empty variable tag"209 nodelist.append(VariableNode(token.contents))210 elif token.token_type == TOKEN_BLOCK:211 if token.contents in parse_until:212 # put token back on token list so calling code knows why it terminated213 self.prepend_token(token)214 return nodelist215 try:216 command = token.contents.split()[0]217 except IndexError:218 raise TemplateSyntaxError, "Empty block tag"219 try:220 # execute callback function for this tag and append resulting node221 nodelist.append(registered_tags[command](self, token))222 except KeyError:223 raise TemplateSyntaxError, "Invalid block tag: '%s'" % command224 if parse_until:225 raise TemplateSyntaxError, "Unclosed tag(s): '%s'" % ', '.join(parse_until)226 return nodelist227 228 def next_token(self):229 return self.tokens.pop(0)230 231 def prepend_token(self, token):232 self.tokens.insert(0, token)233 234 def delete_first_token(self):235 del self.tokens[0]236 237 class FilterParser:238 """Parse a variable token and its optional filters (all as a single string),239 and return a list of tuples of the filter name and arguments.240 Sample:241 >>> token = 'variable|default:"Default value"|date:"Y-m-d"'242 >>> p = FilterParser(token)243 >>> p.filters244 [('default', 'Default value'), ('date', 'Y-m-d')]245 >>> p.var246 'variable'247 248 This class should never be instantiated outside of the249 get_filters_from_token helper function.250 """251 def __init__(self, s):252 self.s = s253 self.i = -1254 self.current = ''255 self.filters = []256 self.current_filter_name = None257 self.current_filter_arg = None258 # First read the variable part259 self.var = self.read_alphanumeric_token()260 if not self.var:261 raise TemplateSyntaxError, "Could not read variable name: '%s'" % self.s262 if self.var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or self.var[0] == '_':263 raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % self.var264 # Have we reached the end?265 if self.current is None:266 return267 if self.current != FILTER_SEPARATOR:268 raise TemplateSyntaxError, "Bad character (expecting '%s') '%s'" % (FILTER_SEPARATOR, self.current)269 # We have a filter separator; start reading the filters270 self.read_filters()271 272 def next_char(self):273 self.i = self.i + 1274 try:275 self.current = self.s[self.i]276 except IndexError:277 self.current = None278 279 def read_alphanumeric_token(self):280 """Read a variable name or filter name, which are continuous strings of281 alphanumeric characters + the underscore"""282 var = ''283 while 1:284 self.next_char()285 if self.current is None:286 break287 if self.current not in ALLOWED_VARIABLE_CHARS:288 break289 var += self.current290 return var291 292 def read_filters(self):293 while 1:294 filter_name, arg = self.read_filter()295 if not registered_filters.has_key(filter_name):296 raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name297 if registered_filters[filter_name][1] == True and arg is None:298 raise TemplateSyntaxError, "Filter '%s' requires an argument" % filter_name299 if registered_filters[filter_name][1] == False and arg is not None:300 raise TemplateSyntaxError, "Filter '%s' should not have an argument (argument is %r)" % (filter_name, arg)301 self.filters.append((filter_name, arg))302 if self.current is None:303 break304 305 def read_filter(self):306 self.current_filter_name = self.read_alphanumeric_token()307 self.current_filter_arg = None308 # Have we reached the end?309 if self.current is None:310 return (self.current_filter_name, None)311 # Does the filter have an argument?312 if self.current == FILTER_ARGUMENT_SEPARATOR:313 self.current_filter_arg = self.read_arg()314 return (self.current_filter_name, self.current_filter_arg)315 # Next thing MUST be a pipe316 if self.current != FILTER_SEPARATOR:317 raise TemplateSyntaxError, "Bad character (expecting '%s') '%s'" % (FILTER_SEPARATOR, self.current)318 return (self.current_filter_name, self.current_filter_arg)319 320 def read_arg(self):321 # First read a "322 self.next_char()323 if self.current != '"':324 raise TemplateSyntaxError, "Bad character (expecting '\"') '%s'" % self.current325 self.escaped = False326 arg = ''327 while 1:328 self.next_char()329 if self.current == '"' and not self.escaped:330 break331 if self.current == '\\' and not self.escaped:332 self.escaped = True333 continue334 if self.current == '\\' and self.escaped:335 arg += '\\'336 self.escaped = False337 continue338 if self.current == '"' and self.escaped:339 arg += '"'340 self.escaped = False341 continue342 if self.escaped and self.current not in '\\"':343 raise TemplateSyntaxError, "Unescaped backslash in '%s'" % self.s344 if self.current is None:345 raise TemplateSyntaxError, "Unexpected end of argument in '%s'" % self.s346 arg += self.current347 # self.current must now be '"'348 self.next_char()349 return arg350 351 def get_filters_from_token(token):352 "Convenient wrapper for FilterParser"353 p = FilterParser(token)354 return (p.var, p.filters)355 356 def resolve_variable(path, context):357 """358 Returns the resolved variable, which may contain attribute syntax, within359 the given context. The variable may be a hard-coded string (if it begins360 and ends with single or double quote marks).361 362 >>> c = {'article': {'section':'News'}}363 >>> resolve_variable('article.section', c)364 'News'365 >>> resolve_variable('article', c)366 {'section': 'News'}367 >>> class AClass: pass368 >>> c = AClass()369 >>> c.article = AClass()370 >>> c.article.section = 'News'371 >>> resolve_variable('article.section', c)372 'News'373 374 (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')375 """376 if path[0] in ('"', "'") and path[0] == path[-1]:377 current = path[1:-1]378 else:379 current = context380 bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)381 while bits:382 try: # dictionary lookup383 current = current[bits[0]]384 except (TypeError, AttributeError, KeyError):385 try: # attribute lookup386 current = getattr(current, bits[0])387 if callable(current):388 if getattr(current, 'alters_data', False):389 current = ''390 else:391 try: # method call (assuming no args required)392 current = current()393 except SilentVariableFailure:394 current = ''395 except TypeError: # arguments *were* required396 current = '' # invalid method call397 except (TypeError, AttributeError):398 try: # list-index lookup399 current = current[int(bits[0])]400 except (IndexError, ValueError, KeyError):401 raise VariableDoesNotExist, "Failed lookup for key [%s] in %r" % (bits[0], current) # missing attribute402 del bits[0]403 return current404 405 def resolve_variable_with_filters(var_string, context):406 """407 var_string is a full variable expression with optional filters, like:408 a.b.c|lower|date:"y/m/d"409 This function resolves the variable in the context, applies all filters and410 returns the object.411 """412 var, filters = get_filters_from_token(var_string)413 try:414 obj = resolve_variable(var, context)415 except VariableDoesNotExist:416 obj = ''417 for name, arg in filters:418 obj = registered_filters[name][0](obj, arg)419 return obj420 421 class Node:422 def render(self, context):423 "Return the node rendered as a string"424 pass425 426 def __iter__(self):427 yield self428 429 def get_nodes_by_type(self, nodetype):430 "Return a list of all nodes (within this node and its nodelist) of the given type"431 nodes = []432 if isinstance(self, nodetype):433 nodes.append(self)434 if hasattr(self, 'nodelist'):435 nodes.extend(self.nodelist.get_nodes_by_type(nodetype))436 return nodes437 438 class NodeList(list):439 def render(self, context):440 bits = []441 for node in self:442 if isinstance(node, Node):443 bits.append(node.render(context))444 else:445 bits.append(node)446 return ''.join(bits)447 448 def get_nodes_by_type(self, nodetype):449 "Return a list of all nodes of the given type"450 nodes = []451 for node in self:452 nodes.extend(node.get_nodes_by_type(nodetype))453 return nodes454 455 class TextNode(Node):456 def __init__(self, s):457 self.s = s458 459 def __repr__(self):460 return "<Text Node: '%s'>" % self.s[:25]461 462 def render(self, context):463 return self.s464 465 class VariableNode(Node):466 def __init__(self, var_string):467 self.var_string = var_string468 469 def __repr__(self):470 return "<Variable Node: %s>" % self.var_string471 472 def render(self, context):473 output = resolve_variable_with_filters(self.var_string, context)474 # Check type so that we don't run str() on a Unicode object475 if not isinstance(output, basestring):476 output = str(output)477 elif isinstance(output, unicode):478 output = output.encode(DEFAULT_CHARSET)479 return output480 481 def register_tag(token_command, callback_function):482 registered_tags[token_command] = callback_function483 484 def unregister_tag(token_command):485 del registered_tags[token_command]486 487 def register_filter(filter_name, callback_function, has_arg):488 registered_filters[filter_name] = (callback_function, has_arg)489 490 def unregister_filter(filter_name):491 del registered_filters[filter_name]492 493 import defaulttags494 import defaultfilters -
django/core/defaultfilters.py
1 "Default variable filters"2 3 import template, re4 import random as random_module5 6 ###################7 # STRINGS #8 ###################9 10 def addslashes(value, _):11 "Adds slashes - useful for passing strings to JavaScript, for example."12 return value.replace('"', '\\"').replace("'", "\\'")13 14 def capfirst(value, _):15 "Capitalizes the first character of the value"16 value = str(value)17 return value and value[0].upper() + value[1:]18 19 def fix_ampersands(value, _):20 "Replaces ampersands with ``&`` entities"21 from django.utils.html import fix_ampersands22 return fix_ampersands(value)23 24 def floatformat(text, _):25 """26 Displays a floating point number as 34.2 (with one decimal place) - but27 only if there's a point to be displayed28 """29 from math import modf30 if not text:31 return ''32 if modf(float(text))[0] < 0.1:33 return text34 return "%.1f" % float(text)35 36 def linenumbers(value, _):37 "Displays text with line numbers"38 from django.utils.html import escape39 lines = value.split('\n')40 # Find the maximum width of the line count, for use with zero padding string format command41 width = str(len(str(len(lines))))42 for i, line in enumerate(lines):43 lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))44 return '\n'.join(lines)45 46 def lower(value, _):47 "Converts a string into all lowercase"48 return value.lower()49 50 def make_list(value, _):51 """52 Returns the value turned into a list. For an integer, it's a list of53 digits. For a string, it's a list of characters.54 """55 return list(str(value))56 57 def slugify(value, _):58 "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"59 value = re.sub('[^\w\s-]', '', value).strip().lower()60 return re.sub('\s+', '-', value)61 62 def stringformat(value, arg):63 """64 Formats the variable according to the argument, a string formatting specifier.65 This specifier uses Python string formating syntax, with the exception that66 the leading "%" is dropped.67 68 See http://docs.python.org/lib/typesseq-strings.html for documentation69 of Python string formatting70 """71 try:72 return ("%" + arg) % value73 except (ValueError, TypeError):74 return ""75 76 def title(value, _):77 "Converts a string into titlecase"78 return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())79 80 def truncatewords(value, arg):81 """82 Truncates a string after a certain number of words83 84 Argument: Number of words to truncate after85 """86 from django.utils.text import truncate_words87 try:88 length = int(arg)89 except ValueError: # invalid literal for int()90 return value # Fail silently.91 if not isinstance(value, basestring):92 value = str(value)93 return truncate_words(value, length)94 95 def upper(value, _):96 "Converts a string into all uppercase"97 return value.upper()98 99 def urlencode(value, _):100 "Escapes a value for use in a URL"101 import urllib102 return urllib.quote(value)103 104 def urlize(value, _):105 "Converts URLs in plain text into clickable links"106 from django.utils.html import urlize107 return urlize(value, nofollow=True)108 109 def urlizetrunc(value, limit):110 """111 Converts URLs into clickable links, truncating URLs to the given character limit112 113 Argument: Length to truncate URLs to.114 """115 from django.utils.html import urlize116 return urlize(value, trim_url_limit=int(limit), nofollow=True)117 118 def wordcount(value, _):119 "Returns the number of words"120 return len(value.split())121 122 def wordwrap(value, arg):123 """124 Wraps words at specified line length125 126 Argument: number of words to wrap the text at.127 """128 from django.utils.text import wrap129 return wrap(value, int(arg))130 131 def ljust(value, arg):132 """133 Left-aligns the value in a field of a given width134 135 Argument: field size136 """137 return str(value).ljust(int(arg))138 139 def rjust(value, arg):140 """141 Right-aligns the value in a field of a given width142 143 Argument: field size144 """145 return str(value).rjust(int(arg))146 147 def center(value, arg):148 "Centers the value in a field of a given width"149 return str(value).center(int(arg))150 151 def cut(value, arg):152 "Removes all values of arg from the given string"153 return value.replace(arg, '')154 155 ###################156 # HTML STRINGS #157 ###################158 159 def escape(value, _):160 "Escapes a string's HTML"161 from django.utils.html import escape162 return escape(value)163 164 def linebreaks(value, _):165 "Converts newlines into <p> and <br />s"166 from django.utils.html import linebreaks167 return linebreaks(value)168 169 def linebreaksbr(value, _):170 "Converts newlines into <br />s"171 return value.replace('\n', '<br />')172 173 def removetags(value, tags):174 "Removes a space separated list of [X]HTML tags from the output"175 tags = [re.escape(tag) for tag in tags.split()]176 tags_re = '(%s)' % '|'.join(tags)177 starttag_re = re.compile('<%s(>|(\s+[^>]*>))' % tags_re)178 endtag_re = re.compile('</%s>' % tags_re)179 value = starttag_re.sub('', value)180 value = endtag_re.sub('', value)181 return value182 183 def striptags(value, _):184 "Strips all [X]HTML tags"185 from django.utils.html import strip_tags186 if not isinstance(value, basestring):187 value = str(value)188 return strip_tags(value)189 190 ###################191 # LISTS #192 ###################193 194 def dictsort(value, arg):195 """196 Takes a list of dicts, returns that list sorted by the property given in197 the argument.198 """199 decorated = [(template.resolve_variable('var.' + arg, {'var' : item}), item) for item in value]200 decorated.sort()201 return [item[1] for item in decorated]202 203 def dictsortreversed(value, arg):204 """205 Takes a list of dicts, returns that list sorted in reverse order by the206 property given in the argument.207 """208 decorated = [(template.resolve_variable('var.' + arg, {'var' : item}), item) for item in value]209 decorated.sort()210 decorated.reverse()211 return [item[1] for item in decorated]212 213 def first(value, _):214 "Returns the first item in a list"215 try:216 return value[0]217 except IndexError:218 return ''219 220 def join(value, arg):221 "Joins a list with a string, like Python's ``str.join(list)``"222 try:223 return arg.join(map(str, value))224 except AttributeError: # fail silently but nicely225 return value226 227 def length(value, _):228 "Returns the length of the value - useful for lists"229 return len(value)230 231 def length_is(value, arg):232 "Returns a boolean of whether the value's length is the argument"233 return len(value) == int(arg)234 235 def random(value, _):236 "Returns a random item from the list"237 return random_module.choice(value)238 239 def slice_(value, arg):240 """241 Returns a slice of the list.242 243 Uses the same syntax as Python's list slicing; see244 http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice245 for an introduction.246 """247 try:248 return value[slice(*[x and int(x) or None for x in arg.split(':')])]249 except (ValueError, TypeError):250 return value # Fail silently.251 252 def unordered_list(value, _):253 """254 Recursively takes a self-nested list and returns an HTML unordered list --255 WITHOUT opening and closing <ul> tags.256 257 The list is assumed to be in the proper format. For example, if ``var`` contains258 ``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,259 then ``{{ var|unordered_list }}`` would return::260 261 <li>States262 <ul>263 <li>Kansas264 <ul>265 <li>Lawrence</li>266 <li>Topeka</li>267 </ul>268 </li>269 <li>Illinois</li>270 </ul>271 </li>272 """273 def _helper(value, tabs):274 indent = '\t' * tabs275 if value[1]:276 return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, value[0], indent,277 '\n'.join([unordered_list(v, tabs+1) for v in value[1]]), indent, indent)278 else:279 return '%s<li>%s</li>' % (indent, value[0])280 return _helper(value, 1)281 282 ###################283 # INTEGERS #284 ###################285 286 def add(value, arg):287 "Adds the arg to the value"288 return int(value) + int(arg)289 290 def get_digit(value, arg):291 """292 Given a whole number, returns the requested digit of it, where 1 is the293 right-most digit, 2 is the second-right-most digit, etc. Returns the294 original value for invalid input (if input or argument is not an integer,295 or if argument is less than 1). Otherwise, output is always an integer.296 """297 try:298 arg = int(arg)299 value = int(value)300 except ValueError:301 return value # Fail silently for an invalid argument302 if arg < 1:303 return value304 try:305 return int(str(value)[-arg])306 except IndexError:307 return 0308 309 ###################310 # DATES #311 ###################312 313 def date(value, arg):314 "Formats a date according to the given format"315 from django.utils.dateformat import format316 return format(value, arg)317 318 def time(value, arg):319 "Formats a time according to the given format"320 from django.utils.dateformat import time_format321 return time_format(value, arg)322 323 def timesince(value, _):324 'Formats a date as the time since that date (i.e. "4 days, 6 hours")'325 from django.utils.timesince import timesince326 return timesince(value)327 328 ###################329 # LOGIC #330 ###################331 332 def default(value, arg):333 "If value is unavailable, use given default"334 return value or arg335 336 def default_if_none(value, arg):337 "If value is None, use given default"338 if value is None:339 return arg340 return value341 342 def divisibleby(value, arg):343 "Returns true if the value is devisible by the argument"344 return int(value) % int(arg) == 0345 346 def yesno(value, arg):347 """348 Given a string mapping values for true, false and (optionally) None,349 returns one of those strings accoding to the value:350 351 ========== ====================== ==================================352 Value Argument Outputs353 ========== ====================== ==================================354 ``True`` ``"yeah,no,maybe"`` ``yeah``355 ``False`` ``"yeah,no,maybe"`` ``no``356 ``None`` ``"yeah,no,maybe"`` ``maybe``357 ``None`` ``"yeah,no"`` ``"no"`` (converts None to False358 if no mapping for None is given.359 ========== ====================== ==================================360 """361 bits = arg.split(',')362 if len(bits) < 2:363 return value # Invalid arg.364 try:365 yes, no, maybe = bits366 except ValueError: # unpack list of wrong size (no "maybe" value provided)367 yes, no, maybe = bits[0], bits[1], bits[1]368 if value is None:369 return maybe370 if value:371 return yes372 return no373 374 ###################375 # MISC #376 ###################377 378 def filesizeformat(bytes, _):379 """380 Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102381 bytes, etc).382 """383 bytes = float(bytes)384 if bytes < 1024:385 return "%d byte%s" % (bytes, bytes != 1 and 's' or '')386 if bytes < 1024 * 1024:387 return "%.1f KB" % (bytes / 1024)388 if bytes < 1024 * 1024 * 1024:389 return "%.1f MB" % (bytes / (1024 * 1024))390 return "%.1f GB" % (bytes / (1024 * 1024 * 1024))391 392 def pluralize(value, _):393 "Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'"394 try:395 if int(value) != 1:396 return 's'397 except ValueError: # invalid string that's not a number398 pass399 except TypeError: # value isn't a string or a number; maybe it's a list?400 try:401 if len(value) != 1:402 return 's'403 except TypeError: # len() of unsized object404 pass405 return ''406 407 def phone2numeric(value, _):408 "Takes a phone number and converts it in to its numerical equivalent"409 from django.utils.text import phone2numeric410 return phone2numeric(value)411 412 def pprint(value, _):413 "A wrapper around pprint.pprint -- for debugging, really"414 from pprint import pformat415 return pformat(value)416 417 # Syntax: template.register_filter(name of filter, callback, has_argument)418 template.register_filter('add', add, True)419 template.register_filter('addslashes', addslashes, False)420 template.register_filter('capfirst', capfirst, False)421 template.register_filter('center', center, True)422 template.register_filter('cut', cut, True)423 template.register_filter('date', date, True)424 template.register_filter('default', default, True)425 template.register_filter('dictsort', dictsort, True)426 template.register_filter('dictsortreversed', dictsortreversed, True)427 template.register_filter('divisibleby', divisibleby, True)428 template.register_filter('escape', escape, False)429 template.register_filter('filesizeformat', filesizeformat, False)430 template.register_filter('first', first, False)431 template.register_filter('fix_ampersands', fix_ampersands, False)432 template.register_filter('floatformat', floatformat, False)433 template.register_filter('get_digit', get_digit, True)434 template.register_filter('join', join, True)435 template.register_filter('length', length, False)436 template.register_filter('length_is', length_is, True)437 template.register_filter('linebreaks', linebreaks, False)438 template.register_filter('linebreaksbr', linebreaksbr, False)439 template.register_filter('linenumbers', linenumbers, False)440 template.register_filter('ljust', ljust, True)441 template.register_filter('lower', lower, False)442 template.register_filter('make_list', make_list, False)443 template.register_filter('phone2numeric', phone2numeric, False)444 template.register_filter('pluralize', pluralize, False)445 template.register_filter('pprint', pprint, False)446 template.register_filter('removetags', removetags, True)447 template.register_filter('random', random, False)448 template.register_filter('rjust', rjust, True)449 template.register_filter('slice', slice_, True)450 template.register_filter('slugify', slugify, False)451 template.register_filter('stringformat', stringformat, True)452 template.register_filter('striptags', striptags, False)453 template.register_filter('time', time, True)454 template.register_filter('timesince', timesince, False)455 template.register_filter('title', title, False)456 template.register_filter('truncatewords', truncatewords, True)457 template.register_filter('unordered_list', unordered_list, False)458 template.register_filter('upper', upper, False)459 template.register_filter('urlencode', urlencode, False)460 template.register_filter('urlize', urlize, False)461 template.register_filter('urlizetrunc', urlizetrunc, True)462 template.register_filter('wordcount', wordcount, False)463 template.register_filter('wordwrap', wordwrap, True)464 template.register_filter('yesno', yesno, True) -
django/core/template/defaultfilters.py
1 1 "Default variable filters" 2 2 3 import template, re 3 from django.core.template import resolve_variable 4 # from django.core import template 5 import re 4 6 import random as random_module 5 7 6 8 ################### -
django/core/template/defaulttags.py
1 1 "Default tags used by the template system, available to all templates." 2 2 3 from django.core import template 3 4 import sys 4 import template5 5 6 6 class CommentNode(template.Node): 7 7 def render(self, context): … … 434 434 ========================== ================================================ 435 435 ``forloop.counter`` The current iteration of the loop (1-indexed) 436 436 ``forloop.counter0`` The current iteration of the loop (0-indexed) 437 ``forloop.revcounter`` The number of iterations from the end of the 437 ``forloop.revcounter`` The number of iterations from the end of the 438 438 loop (1-indexed) 439 ``forloop.revcounter0`` The number of iterations from the end of the 439 ``forloop.revcounter0`` The number of iterations from the end of the 440 440 loop (0-indexed) 441 441 ``forloop.first`` True if this is the first time through the loop 442 442 ``forloop.last`` True if this is the last time through the loop