diff -r 6641e941ef1e -r ff1a9aa48cfd app/django/template/defaulttags.py --- a/app/django/template/defaulttags.py Tue Oct 14 12:36:55 2008 +0000 +++ b/app/django/template/defaulttags.py Tue Oct 14 16:00:59 2008 +0000 @@ -39,12 +39,11 @@ class CycleNode(Node): def __init__(self, cyclevars, variable_name=None): - self.cycle_iter = itertools_cycle(cyclevars) + self.cycle_iter = itertools_cycle([Variable(v) for v in cyclevars]) self.variable_name = variable_name def render(self, context): - value = self.cycle_iter.next() - value = Variable(value).resolve(context) + value = self.cycle_iter.next().resolve(context) if self.variable_name: context[self.variable_name] = value return value @@ -158,34 +157,37 @@ return nodelist.render(context) class IfChangedNode(Node): - def __init__(self, nodelist, *varlist): - self.nodelist = nodelist + def __init__(self, nodelist_true, nodelist_false, *varlist): + self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false self._last_seen = None self._varlist = map(Variable, varlist) + self._id = str(id(self)) def render(self, context): - if 'forloop' in context and context['forloop']['first']: + if 'forloop' in context and self._id not in context['forloop']: self._last_seen = None + context['forloop'][self._id] = 1 try: if self._varlist: # Consider multiple parameters. This automatically behaves # like an OR evaluation of the multiple variables. compare_to = [var.resolve(context) for var in self._varlist] else: - compare_to = self.nodelist.render(context) + compare_to = self.nodelist_true.render(context) except VariableDoesNotExist: compare_to = None - if compare_to != self._last_seen: + if compare_to != self._last_seen: firstloop = (self._last_seen == None) self._last_seen = compare_to context.push() context['ifchanged'] = {'firstloop': firstloop} - content = self.nodelist.render(context) + content = self.nodelist_true.render(context) context.pop() return content - else: - return '' + elif self.nodelist_false: + return self.nodelist_false.render(context) + return '' class IfEqualNode(Node): def __init__(self, var1, var2, nodelist_true, nodelist_false, negate): @@ -349,25 +351,40 @@ return self.mapping.get(self.tagtype, '') class URLNode(Node): - def __init__(self, view_name, args, kwargs): + def __init__(self, view_name, args, kwargs, asvar): self.view_name = view_name self.args = args self.kwargs = kwargs + self.asvar = asvar def render(self, context): from django.core.urlresolvers import reverse, NoReverseMatch args = [arg.resolve(context) for arg in self.args] kwargs = dict([(smart_str(k,'ascii'), v.resolve(context)) for k, v in self.kwargs.items()]) + + + # Try to look up the URL twice: once given the view name, and again + # relative to what we guess is the "main" app. If they both fail, + # re-raise the NoReverseMatch unless we're using the + # {% url ... as var %} construct in which cause return nothing. + url = '' try: - return reverse(self.view_name, args=args, kwargs=kwargs) + url = reverse(self.view_name, args=args, kwargs=kwargs) except NoReverseMatch: + project_name = settings.SETTINGS_MODULE.split('.')[0] try: - project_name = settings.SETTINGS_MODULE.split('.')[0] - return reverse(project_name + '.' + self.view_name, - args=args, kwargs=kwargs) + url = reverse(project_name + '.' + self.view_name, + args=args, kwargs=kwargs) except NoReverseMatch: - return '' + if self.asvar is None: + raise + + if self.asvar: + context[self.asvar] = url + return '' + else: + return url class WidthRatioNode(Node): def __init__(self, val_expr, max_expr, max_width): @@ -452,17 +469,17 @@ ... ... - You can use any number of values, seperated by spaces. Commas can also + You can use any number of values, separated by spaces. Commas can also be used to separate values; if a comma is used, the cycle values are interpreted as literal strings. """ # Note: This returns the exact same node on each {% cycle name %} call; # that is, the node object returned from {% cycle a b c as name %} and the - # one returned from {% cycle name %} are the exact same object. This + # one returned from {% cycle name %} are the exact same object. This # shouldn't cause problems (heh), but if it does, now you know. # - # Ugly hack warning: this stuffs the named template dict into parser so + # Ugly hack warning: This stuffs the named template dict into parser so # that names are only unique within each template (as opposed to using # a global variable, which would make cycle names have to be unique across # *all* templates. @@ -481,8 +498,7 @@ # {% cycle foo %} case. name = args[1] if not hasattr(parser, '_namedCycleNodes'): - raise TemplateSyntaxError("No named cycles in template." - " '%s' is not defined" % name) + raise TemplateSyntaxError("No named cycles in template. '%s' is not defined" % name) if not name in parser._namedCycleNodes: raise TemplateSyntaxError("Named cycle '%s' does not exist" % name) return parser._namedCycleNodes[name] @@ -682,8 +698,10 @@ def do_if(parser, token): """ The ``{% if %}`` tag evaluates a variable, and if that variable is "true" - (i.e. exists, is not empty, and is not a false boolean value) the contents - of the block are output:: + (i.e., exists, is not empty, and is not a false boolean value), the + contents of the block are output: + + :: {% if athlete_list %} Number of athletes: {{ athlete_list|count }} @@ -801,9 +819,14 @@ {% endfor %} """ bits = token.contents.split() - nodelist = parser.parse(('endifchanged',)) - parser.delete_first_token() - return IfChangedNode(nodelist, *bits[1:]) + nodelist_true = parser.parse(('else', 'endifchanged')) + token = parser.next_token() + if token.contents == 'else': + nodelist_false = parser.parse(('endifchanged',)) + parser.delete_first_token() + else: + nodelist_false = NodeList() + return IfChangedNode(nodelist_true, nodelist_false, *bits[1:]) ifchanged = register.tag(ifchanged) #@register.tag @@ -1036,21 +1059,30 @@ The URL will look like ``/clients/client/123/``. """ - bits = token.contents.split(' ', 2) + bits = token.contents.split(' ') if len(bits) < 2: raise TemplateSyntaxError("'%s' takes at least one argument" " (path to a view)" % bits[0]) + viewname = bits[1] args = [] kwargs = {} + asvar = None + if len(bits) > 2: - for arg in bits[2].split(','): - if '=' in arg: - k, v = arg.split('=', 1) - k = k.strip() - kwargs[k] = parser.compile_filter(v) + bits = iter(bits[2:]) + for bit in bits: + if bit == 'as': + asvar = bits.next() + break else: - args.append(parser.compile_filter(arg)) - return URLNode(bits[1], args, kwargs) + for arg in bit.split(","): + if '=' in arg: + k, v = arg.split('=', 1) + k = k.strip() + kwargs[k] = parser.compile_filter(v) + elif arg: + args.append(parser.compile_filter(arg)) + return URLNode(viewname, args, kwargs, asvar) url = register.tag(url) #@register.tag