Ticket #13910: django-1.2.1.diff

File django-1.2.1.diff, 20.3 KB (added by Ron Panduwana, 14 years ago)

diff with django-1.2.1 source code

  • django/shortcuts/__init__.py

     
    1111from django.db.models.query import QuerySet
    1212from django.core import urlresolvers
    1313
     14def http_response_using(func, *args, **kwargs):
     15    """
     16    Returns a HttpResponse whose content is filled with the result of calling
     17    func() with the passed arguments.
     18    """
     19    httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)}
     20    return HttpResponse(func(*args, **kwargs), **httpresponse_kwargs)
     21
    1422def render_to_response(*args, **kwargs):
    1523    """
    1624    Returns a HttpResponse whose content is filled with the result of calling
    1725    django.template.loader.render_to_string() with the passed arguments.
    1826    """
    19     httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)}
    20     return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)
     27    return http_response_using(loader.render_to_string, *args, **kwargs)
    2128
     29def stream_to_response(*args, **kwargs):
     30    """
     31    Returns a HttpResponse whose content is filled with the result of calling
     32    django.template.loader.get_render_stream() with the passed arguments.
     33    """
     34    return http_response_using(loader.stream, *args, **kwargs)
     35
    2236def redirect(to, *args, **kwargs):
    2337    """
    2438    Returns an HttpResponseRedirect to the apropriate URL for the arguments
  • django/template/__init__.py

     
    166166    def _render(self, context):
    167167        return self.nodelist.render(context)
    168168
     169    def _stream(self, context):
     170        return self.nodelist.stream(context)
     171
    169172    def render(self, context):
    170173        "Display stage -- can be called many times"
     174        return ''.join(self.stream(context))
     175
     176    def stream(self, context):
    171177        context.render_context.push()
    172178        try:
    173             return self._render(context)
     179            return self._stream(context)
    174180        finally:
    175181            context.render_context.pop()
    176182
     
    770776        "Return the node rendered as a string"
    771777        pass
    772778
     779    def stream(self, context):
     780        "Returns a generator that progressively renders the node"
     781        yield self.render(context)
     782
    773783    def __iter__(self):
    774784        yield self
    775785
     
    790800    contains_nontext = False
    791801
    792802    def render(self, context):
    793         bits = []
     803        return ''.join(self.stream(context))
     804       
     805    def stream(self, context):
    794806        for node in self:
    795807            if isinstance(node, Node):
    796                 bits.append(self.render_node(node, context))
     808                for bit in self.stream_node(node, context):
     809                    yield mark_safe(force_unicode(bit))
    797810            else:
    798                 bits.append(node)
    799         return mark_safe(''.join([force_unicode(b) for b in bits]))
     811                yield mark_safe(force_unicode(node))
    800812
    801813    def get_nodes_by_type(self, nodetype):
    802814        "Return a list of all nodes of the given type"
     
    808820    def render_node(self, node, context):
    809821        return node.render(context)
    810822
     823    def stream_node(self, node, context):
     824        return node.stream(context)
     825
    811826class TextNode(Node):
    812827    def __init__(self, s):
    813828        self.s = s
  • django/template/defaulttags.py

     
    2020    """Implements the actions of the autoescape tag."""
    2121    def __init__(self, setting, nodelist):
    2222        self.setting, self.nodelist = setting, nodelist
    23 
    24     def render(self, context):
     23       
     24    def stream(self, context):
    2525        old_setting = context.autoescape
    2626        context.autoescape = self.setting
    27         output = self.nodelist.render(context)
    28         context.autoescape = old_setting
    2927        if self.setting:
    30             return mark_safe(output)
     28            for chunk in self.nodelist.stream(context):
     29                yield mark_safe(chunk)
    3130        else:
    32             return output
     31            for chunk in self.nodelist.stream(context):
     32                yield chunk
     33        context.autoescape = old_setting
    3334
     35    def render(self, context):
     36        ''.join(self.stream(context))
     37
    3438class CommentNode(Node):
    3539    def render(self, context):
    3640        return ''
     
    7983        self.filter_expr, self.nodelist = filter_expr, nodelist
    8084
    8185    def render(self, context):
    82         output = self.nodelist.render(context)
    83         # Apply filters.
    84         context.update({'var': output})
    85         filtered = self.filter_expr.resolve(context)
     86        return ''.join(self.stream(context))
     87
     88    def stream(self, context):
     89        for output in self.nodelist.stream(context):
     90            # Apply filters.
     91            context.update({'var': output})
     92            filtered = self.filter_expr.resolve(context)
     93            yield filtered
    8694        context.pop()
    87         return filtered
    8895
    8996class FirstOfNode(Node):
    9097    def __init__(self, vars):
     
    122129            yield node
    123130
    124131    def render(self, context):
     132        return ''.join(self.stream(context))
     133
     134    def stream(self, context):
    125135        if 'forloop' in context:
    126136            parentloop = context['forloop']
    127137        else:
     
    133143            values = []
    134144        if values is None:
    135145            values = []
    136         if not hasattr(values, '__len__'):
    137             values = list(values)
    138         len_values = len(values)
    139         if len_values < 1:
    140             context.pop()
    141             return self.nodelist_empty.render(context)
    142         nodelist = NodeList()
    143         if self.is_reversed:
    144             values = reversed(values)
     146        if 'generator' == values.__class__.__name__:
     147            if self.is_reversed:
     148                values = list(values)
     149                len_values = len(values)
     150                values = reversed(values)
     151            else:
     152                len_values = 0 # generators have unknown len, but if the len is needed, convert the
     153                               # generator to list
     154                for node in self.nodelist_loop:
     155                    try:
     156                        token = node.filter_expression.token
     157                    except AttributeError:
     158                        continue
     159                    else:
     160                        if token.startswith('forloop.revcounter') or token.startswith('forloop.last'):
     161                            values = list(values)
     162                            len_values = len(values)
     163                            break
     164        else: # not generator
     165            if not hasattr(values, '__len__'):
     166                values = list(values)
     167            len_values = len(values)
     168            if self.is_reversed:
     169                values = reversed(values)
    145170        unpack = len(self.loopvars) > 1
    146171        # Create a forloop value in the context.  We'll update counters on each
    147172        # iteration just below.
    148173        loop_dict = context['forloop'] = {'parentloop': parentloop}
     174        i = None
    149175        for i, item in enumerate(values):
    150176            # Shortcuts for current loop iteration number.
    151177            loop_dict['counter0'] = i
     
    163189                context.update(dict(zip(self.loopvars, item)))
    164190            else:
    165191                context[self.loopvars[0]] = item
    166             for node in self.nodelist_loop:
    167                 nodelist.append(node.render(context))
     192            for chunk in self.nodelist_loop.stream(context):
     193                yield chunk
    168194            if unpack:
    169195                # The loop variables were pushed on to the context so pop them
    170196                # off again. This is necessary because the tag lets the length
     
    173199                # context.
    174200                context.pop()
    175201        context.pop()
    176         return nodelist.render(context)
     202        if i is None: # values was empty
     203            for chunk in self.nodelist_empty.stream(context):
     204                yield chunk
    177205
    178206class IfChangedNode(Node):
    179207    child_nodelists = ('nodelist_true', 'nodelist_false')
     
    219247        return "<IfEqualNode>"
    220248
    221249    def render(self, context):
     250        return ''.join(self.stream(context))
     251       
     252    def stream(self, context):
    222253        val1 = self.var1.resolve(context, True)
    223254        val2 = self.var2.resolve(context, True)
    224255        if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
    225             return self.nodelist_true.render(context)
    226         return self.nodelist_false.render(context)
     256            return self.nodelist_true.stream(context)
     257        return self.nodelist_false.stream(context)
    227258
    228259class IfNode(Node):
    229260    child_nodelists = ('nodelist_true', 'nodelist_false')
     
    242273            yield node
    243274
    244275    def render(self, context):
     276        return ''.join(self.stream(context))
     277       
     278    def stream(self, context):
    245279        try:
    246280            var = self.var.eval(context)
    247281        except VariableDoesNotExist:
    248282            var = None
    249283
    250284        if var:
    251             return self.nodelist_true.render(context)
     285            return self.nodelist_true.stream(context)
    252286        else:
    253             return self.nodelist_false.render(context)
     287            return self.nodelist_false.stream(context)
    254288
    255289class RegroupNode(Node):
    256290    def __init__(self, target, expression, var_name):
     
    281315class SsiNode(Node):
    282316    def __init__(self, filepath, parsed):
    283317        self.filepath, self.parsed = filepath, parsed
     318       
     319     
     320    def render(self, context):
     321        return ''.join(self.stream(context))
    284322
    285     def render(self, context):
     323    def stream(self, context):
    286324        if not include_is_allowed(self.filepath):
    287325            if settings.DEBUG:
    288                 return "[Didn't have permission to include file]"
     326                yield "[Didn't have permission to include file]"
    289327            else:
    290                 return '' # Fail silently for invalid includes.
     328                yield '' # Fail silently for invalid includes.
     329            return
    291330        try:
    292331            fp = open(self.filepath, 'r')
    293332            output = fp.read()
     
    297336        if self.parsed:
    298337            try:
    299338                t = Template(output, name=self.filepath)
    300                 return t.render(context)
     339                for chunk in t.stream(context):
     340                    yield chunk
    301341            except TemplateSyntaxError, e:
    302342                if settings.DEBUG:
    303                     return "[Included template had syntax error: %s]" % e
     343                    yield "[Included template had syntax error: %s]" % e
    304344                else:
    305                     return '' # Fail silently for invalid included templates.
    306         return output
     345                    yield '' # Fail silently for invalid included templates.
     346        else:
     347            yield output
    307348
    308349class LoadNode(Node):
    309350    def render(self, context):
     
    419460        return "<WithNode>"
    420461
    421462    def render(self, context):
     463        return ''.join(self.stream(context))
     464   
     465    def stream(self, context):
    422466        val = self.var.resolve(context)
    423467        context.push()
    424468        context[self.name] = val
    425         output = self.nodelist.render(context)
     469        for output in self.nodelist.stream(context):
     470            yield output
    426471        context.pop()
    427         return output
    428472
    429473#@register.tag
    430474def autoescape(parser, token):
  • django/template/loader_tags.py

     
    4646        return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
    4747
    4848    def render(self, context):
     49        return ''.join(self.stream(context))
     50
     51    def stream(self, context):
    4952        block_context = context.render_context.get(BLOCK_CONTEXT_KEY)
    5053        context.push()
    5154        if block_context is None:
    5255            context['block'] = self
    53             result = self.nodelist.render(context)
     56            for chunk in self.nodelist.stream(context):
     57                yield chunk
    5458        else:
    5559            push = block = block_context.pop(self.name)
    5660            if block is None:
     
    5963            block = BlockNode(block.name, block.nodelist)
    6064            block.context = context
    6165            context['block'] = block
    62             result = block.nodelist.render(context)
     66            for chunk in block.nodelist.stream(context):
     67                yield chunk
    6368            if push is not None:
    6469                block_context.push(self.name, push)
    6570        context.pop()
    66         return result
    6771
    6872    def super(self):
    6973        render_context = self.context.render_context
     
    100104        return get_template(parent)
    101105
    102106    def render(self, context):
     107        return ''.join(self.stream(context))
     108
     109    def stream(self, context):
    103110        compiled_parent = self.get_parent(context)
    104111
    105112        if BLOCK_CONTEXT_KEY not in context.render_context:
     
    120127                    block_context.add_blocks(blocks)
    121128                break
    122129
    123         # Call Template._render explicitly so the parser context stays
     130        # Call Template._stream explicitly so the parser context stays
    124131        # the same.
    125         return compiled_parent._render(context)
     132        return compiled_parent._stream(context)
    126133
    127134class ConstantIncludeNode(Node):
    128135    def __init__(self, template_path):
     
    133140            if settings.TEMPLATE_DEBUG:
    134141                raise
    135142            self.template = None
     143           
     144    def render(self, context):
     145        ''.join(self.stream(context))
    136146
    137     def render(self, context):
     147    def stream(self, context):
    138148        if self.template:
    139             return self.template.render(context)
     149            return self.template.stream(context)
    140150        else:
    141             return ''
     151            return empty_string_generator()
    142152
    143153class IncludeNode(Node):
    144154    def __init__(self, template_name):
    145155        self.template_name = Variable(template_name)
    146156
    147157    def render(self, context):
     158        ''.join(self.stream(context))
     159
     160    def stream(self, context):
    148161        try:
    149162            template_name = self.template_name.resolve(context)
    150163            t = get_template(template_name)
    151             return t.render(context)
     164            return t.stream(context)
    152165        except TemplateSyntaxError, e:
    153166            if settings.TEMPLATE_DEBUG:
    154167                raise
    155             return ''
     168            return empty_string_generator()
    156169        except:
    157             return '' # Fail silently for invalid included templates.
     170            return empty_string_generator() # Fail silently for invalid included templates.
    158171
    159172def do_block(parser, token):
    160173    """
     
    214227    if path[0] in ('"', "'") and path[-1] == path[0]:
    215228        return ConstantIncludeNode(path[1:-1])
    216229    return IncludeNode(bits[1])
     230   
     231def empty_string_generator():
     232    yield ''
    217233
    218234register.tag('block', do_block)
    219235register.tag('extends', do_extends)
  • django/template/loader.py

     
    174174    get_template, or it may be a tuple to use select_template to find one of
    175175    the templates in the list. Returns a string.
    176176    """
     177    return ''.join(stream(template_name, dictionary, context_instance))
     178
     179def stream(template_name, dictionary=None, context_instance=None):
     180    """
     181    Loads the given template_name and streams it with the given dictionary as
     182    context. The template_name may be a string to load a single template using
     183    get_template, or it may be a tuple to use select_template to find one of
     184    the templates in the list. Returns a string generator.
     185    """
    177186    dictionary = dictionary or {}
    178187    if isinstance(template_name, (list, tuple)):
    179188        t = select_template(template_name)
     
    183192        context_instance.update(dictionary)
    184193    else:
    185194        context_instance = Context(dictionary)
    186     return t.render(context_instance)
     195    return t.stream(context_instance)
    187196
    188197def select_template(template_name_list):
    189198    "Given a list of template names, returns the first that can be loaded."
  • docs/howto/custom-template-tags.txt

     
    932932For more examples of complex rendering, see the source code for ``{% if %}``,
    933933``{% for %}``, ``{% ifequal %}`` and ``{% ifchanged %}``. They live in
    934934``django/template/defaulttags.py``.
     935
     936Add streaming support
     937~~~~~~~~~~~~~~~~~~~~~
     938
     939Simple tag like the earlier example of ``CurrentTimeNode`` does not need to
     940worry about streaming support. But if your custom tag is a more complex one
     941that have a "begin" tag and "end" tag like the above ``{% upper %}`` ..
     942``{% endupper %}``, and you want the tag to be able to progressively render
     943its contents, you can define your own ``stream()`` method for the custom tag.
     944The ``stream()`` method takes the same arguments as ``render()`` but instead
     945of returning string, it needs to return string generator.
     946
     947As an example, the ``stream()`` method for the ``{% upper %}`` tag above will
     948look like this::
     949
     950    def stream(self, context):
     951        for chunk in self.nodelist.stream(context):
     952            yield chunk.upper()
     953           
     954If you define ``stream()`` in your custom template tag, you can define its
     955``render()`` method as ``''.join(self.stream(context))`` to avoid duplicate
     956codes.
  • docs/topics/http/shortcuts.txt

     
    8080        return HttpResponse(t.render(c),
    8181            mimetype="application/xhtml+xml")
    8282
     83``stream_to_response``
     84======================
     85
     86.. function:: stream_to_response(template[, dictionary][, context_instance][, mimetype])
     87
     88   The ``stream_to_response`` shortcut is the same as
     89   :func:`render_to_response`, except that ``stream_to_response`` returns an
     90   :class:`~django.http.HttpResponse` object that gradually renders the
     91   template, while ``render_to_response`` waits for the whole template to be
     92   rendered before putting it into the response.
     93   
     94   This is useful if something inside the template takes a long time to
     95   render. For example, if the template contains the following code::
     96   
     97    {% for item in warehouse %}
     98        Current value: {{ item.calculate_depreciation }}
     99    {% endfor %}
     100   
     101   If there are 100 items in warehouse and ``calculate_depreciation()`` takes
     102   0.5 seconds to complete, ``render_to_response`` will need 50 seconds to
     103   render all items before returning a response, while ``stream_to_response``
     104   will immediately return a response that renders one item every half
     105   seconds.
     106
    83107``redirect``
    84108============
    85109
  • docs/ref/templates/api.txt

     
    207207            self.database_record.delete()
    208208        sensitive_function.alters_data = True
    209209
     210Getting the rendering generator
     211-------------------------------
     212
     213As an alternative to rendering a context all-at-once, you can get the string
     214generator that render the context progressively by calling the ``Template``
     215object's ``stream()`` method, passing the context as its argument. You can
     216then pass the generator to :class:`~django.http.HttpResponse` to get a
     217"streaming" effect on your view.
     218
    210219.. _invalid-template-variables:
    211220
    212221How invalid variables are handled
     
    690699calls ``render_to_string`` and feeds the result into an ``HttpResponse``
    691700suitable for returning directly from a view.
    692701
     702The ``stream()`` shortcut
     703=========================
     704
     705This shortcut is like :func:`render_to_string` but instead of returning
     706the whole rendered text of the template, this shortcut returns a generator
     707that successively renders parts of the template. See
     708:func:`~django.shortcuts.stream_to_response()` for an example of its usage.
     709
    693710Configuring the template system in standalone mode
    694711==================================================
    695712
Back to Top