def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None): self.loopvars, self.sequence = loopvars, sequence self.is_reversed = is_reversed self.nodelist_loop = nodelist_loop if nodelist_empty is None: self.nodelist_empty = NodeList() else: self.nodelist_empty = nodelist_empty
def render(self, context): if "forloop" in context: parentloop = context["forloop"] else: parentloop = {} context.push() try: values = self.sequence.resolve(context, True) except VariableDoesNotExist: values = [] if values is None: values = [] if not hasattr(values, "__len__"): values = list(values) len_values = len(values) if len_values < 1: context.pop() return self.nodelist_empty.render(context) nodelist = NodeList() if self.is_reversed: values = reversed(values) unpack = len(self.loopvars) > 1 # Create a forloop value in the context. We'll update counters on each # iteration just below. loop_dict = context["forloop"] = {"parentloop": parentloop} for i, item in enumerate(values): # Shortcuts for current loop iteration number. loop_dict["counter0"] = i loop_dict["counter"] = i + 1 # Reverse counter iteration numbers. loop_dict["revcounter"] = len_values - i loop_dict["revcounter0"] = len_values - i - 1 # Boolean values designating first and last times through loop. loop_dict["first"] = i == 0 loop_dict["last"] = i == len_values - 1 pop_context = False if unpack: # If there are multiple loop variables, unpack the item into # them. try: unpacked_vars = dict(zip(self.loopvars, item)) except TypeError: pass else: pop_context = True context.update(unpacked_vars) else: context[self.loopvars[0]] = item # In TEMPLATE_DEBUG mode provide source of the node which # actually raised the exception if settings.TEMPLATE_DEBUG: for node in self.nodelist_loop: try: nodelist.append(node.render(context)) except Exception as e: if not hasattr(e, "django_template_source"): e.django_template_source = node.source raise else: for node in self.nodelist_loop: nodelist.append(node.render(context)) if pop_context: # The loop variables were pushed on to the context so pop them # off again. This is necessary because the tag lets the length # of loopvars differ to the length of each set of items and we # don't want to leave any vars from the previous loop on the # context. context.pop() context.pop() return nodelist.render(context)