Ticket #13910: django-1.2.1.diff
File django-1.2.1.diff, 20.3 KB (added by , 14 years ago) |
---|
-
django/shortcuts/__init__.py
11 11 from django.db.models.query import QuerySet 12 12 from django.core import urlresolvers 13 13 14 def 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 14 22 def render_to_response(*args, **kwargs): 15 23 """ 16 24 Returns a HttpResponse whose content is filled with the result of calling 17 25 django.template.loader.render_to_string() with the passed arguments. 18 26 """ 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) 21 28 29 def 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 22 36 def redirect(to, *args, **kwargs): 23 37 """ 24 38 Returns an HttpResponseRedirect to the apropriate URL for the arguments -
django/template/__init__.py
166 166 def _render(self, context): 167 167 return self.nodelist.render(context) 168 168 169 def _stream(self, context): 170 return self.nodelist.stream(context) 171 169 172 def render(self, context): 170 173 "Display stage -- can be called many times" 174 return ''.join(self.stream(context)) 175 176 def stream(self, context): 171 177 context.render_context.push() 172 178 try: 173 return self._ render(context)179 return self._stream(context) 174 180 finally: 175 181 context.render_context.pop() 176 182 … … 770 776 "Return the node rendered as a string" 771 777 pass 772 778 779 def stream(self, context): 780 "Returns a generator that progressively renders the node" 781 yield self.render(context) 782 773 783 def __iter__(self): 774 784 yield self 775 785 … … 790 800 contains_nontext = False 791 801 792 802 def render(self, context): 793 bits = [] 803 return ''.join(self.stream(context)) 804 805 def stream(self, context): 794 806 for node in self: 795 807 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)) 797 810 else: 798 bits.append(node) 799 return mark_safe(''.join([force_unicode(b) for b in bits])) 811 yield mark_safe(force_unicode(node)) 800 812 801 813 def get_nodes_by_type(self, nodetype): 802 814 "Return a list of all nodes of the given type" … … 808 820 def render_node(self, node, context): 809 821 return node.render(context) 810 822 823 def stream_node(self, node, context): 824 return node.stream(context) 825 811 826 class TextNode(Node): 812 827 def __init__(self, s): 813 828 self.s = s -
django/template/defaulttags.py
20 20 """Implements the actions of the autoescape tag.""" 21 21 def __init__(self, setting, nodelist): 22 22 self.setting, self.nodelist = setting, nodelist 23 24 def render(self, context):23 24 def stream(self, context): 25 25 old_setting = context.autoescape 26 26 context.autoescape = self.setting 27 output = self.nodelist.render(context)28 context.autoescape = old_setting29 27 if self.setting: 30 return mark_safe(output) 28 for chunk in self.nodelist.stream(context): 29 yield mark_safe(chunk) 31 30 else: 32 return output 31 for chunk in self.nodelist.stream(context): 32 yield chunk 33 context.autoescape = old_setting 33 34 35 def render(self, context): 36 ''.join(self.stream(context)) 37 34 38 class CommentNode(Node): 35 39 def render(self, context): 36 40 return '' … … 79 83 self.filter_expr, self.nodelist = filter_expr, nodelist 80 84 81 85 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 86 94 context.pop() 87 return filtered88 95 89 96 class FirstOfNode(Node): 90 97 def __init__(self, vars): … … 122 129 yield node 123 130 124 131 def render(self, context): 132 return ''.join(self.stream(context)) 133 134 def stream(self, context): 125 135 if 'forloop' in context: 126 136 parentloop = context['forloop'] 127 137 else: … … 133 143 values = [] 134 144 if values is None: 135 145 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) 145 170 unpack = len(self.loopvars) > 1 146 171 # Create a forloop value in the context. We'll update counters on each 147 172 # iteration just below. 148 173 loop_dict = context['forloop'] = {'parentloop': parentloop} 174 i = None 149 175 for i, item in enumerate(values): 150 176 # Shortcuts for current loop iteration number. 151 177 loop_dict['counter0'] = i … … 163 189 context.update(dict(zip(self.loopvars, item))) 164 190 else: 165 191 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 168 194 if unpack: 169 195 # The loop variables were pushed on to the context so pop them 170 196 # off again. This is necessary because the tag lets the length … … 173 199 # context. 174 200 context.pop() 175 201 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 177 205 178 206 class IfChangedNode(Node): 179 207 child_nodelists = ('nodelist_true', 'nodelist_false') … … 219 247 return "<IfEqualNode>" 220 248 221 249 def render(self, context): 250 return ''.join(self.stream(context)) 251 252 def stream(self, context): 222 253 val1 = self.var1.resolve(context, True) 223 254 val2 = self.var2.resolve(context, True) 224 255 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) 227 258 228 259 class IfNode(Node): 229 260 child_nodelists = ('nodelist_true', 'nodelist_false') … … 242 273 yield node 243 274 244 275 def render(self, context): 276 return ''.join(self.stream(context)) 277 278 def stream(self, context): 245 279 try: 246 280 var = self.var.eval(context) 247 281 except VariableDoesNotExist: 248 282 var = None 249 283 250 284 if var: 251 return self.nodelist_true. render(context)285 return self.nodelist_true.stream(context) 252 286 else: 253 return self.nodelist_false. render(context)287 return self.nodelist_false.stream(context) 254 288 255 289 class RegroupNode(Node): 256 290 def __init__(self, target, expression, var_name): … … 281 315 class SsiNode(Node): 282 316 def __init__(self, filepath, parsed): 283 317 self.filepath, self.parsed = filepath, parsed 318 319 320 def render(self, context): 321 return ''.join(self.stream(context)) 284 322 285 def render(self, context):323 def stream(self, context): 286 324 if not include_is_allowed(self.filepath): 287 325 if settings.DEBUG: 288 return"[Didn't have permission to include file]"326 yield "[Didn't have permission to include file]" 289 327 else: 290 return '' # Fail silently for invalid includes. 328 yield '' # Fail silently for invalid includes. 329 return 291 330 try: 292 331 fp = open(self.filepath, 'r') 293 332 output = fp.read() … … 297 336 if self.parsed: 298 337 try: 299 338 t = Template(output, name=self.filepath) 300 return t.render(context) 339 for chunk in t.stream(context): 340 yield chunk 301 341 except TemplateSyntaxError, e: 302 342 if settings.DEBUG: 303 return"[Included template had syntax error: %s]" % e343 yield "[Included template had syntax error: %s]" % e 304 344 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 307 348 308 349 class LoadNode(Node): 309 350 def render(self, context): … … 419 460 return "<WithNode>" 420 461 421 462 def render(self, context): 463 return ''.join(self.stream(context)) 464 465 def stream(self, context): 422 466 val = self.var.resolve(context) 423 467 context.push() 424 468 context[self.name] = val 425 output = self.nodelist.render(context) 469 for output in self.nodelist.stream(context): 470 yield output 426 471 context.pop() 427 return output428 472 429 473 #@register.tag 430 474 def autoescape(parser, token): -
django/template/loader_tags.py
46 46 return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist) 47 47 48 48 def render(self, context): 49 return ''.join(self.stream(context)) 50 51 def stream(self, context): 49 52 block_context = context.render_context.get(BLOCK_CONTEXT_KEY) 50 53 context.push() 51 54 if block_context is None: 52 55 context['block'] = self 53 result = self.nodelist.render(context) 56 for chunk in self.nodelist.stream(context): 57 yield chunk 54 58 else: 55 59 push = block = block_context.pop(self.name) 56 60 if block is None: … … 59 63 block = BlockNode(block.name, block.nodelist) 60 64 block.context = context 61 65 context['block'] = block 62 result = block.nodelist.render(context) 66 for chunk in block.nodelist.stream(context): 67 yield chunk 63 68 if push is not None: 64 69 block_context.push(self.name, push) 65 70 context.pop() 66 return result67 71 68 72 def super(self): 69 73 render_context = self.context.render_context … … 100 104 return get_template(parent) 101 105 102 106 def render(self, context): 107 return ''.join(self.stream(context)) 108 109 def stream(self, context): 103 110 compiled_parent = self.get_parent(context) 104 111 105 112 if BLOCK_CONTEXT_KEY not in context.render_context: … … 120 127 block_context.add_blocks(blocks) 121 128 break 122 129 123 # Call Template._ renderexplicitly so the parser context stays130 # Call Template._stream explicitly so the parser context stays 124 131 # the same. 125 return compiled_parent._ render(context)132 return compiled_parent._stream(context) 126 133 127 134 class ConstantIncludeNode(Node): 128 135 def __init__(self, template_path): … … 133 140 if settings.TEMPLATE_DEBUG: 134 141 raise 135 142 self.template = None 143 144 def render(self, context): 145 ''.join(self.stream(context)) 136 146 137 def render(self, context):147 def stream(self, context): 138 148 if self.template: 139 return self.template. render(context)149 return self.template.stream(context) 140 150 else: 141 return ''151 return empty_string_generator() 142 152 143 153 class IncludeNode(Node): 144 154 def __init__(self, template_name): 145 155 self.template_name = Variable(template_name) 146 156 147 157 def render(self, context): 158 ''.join(self.stream(context)) 159 160 def stream(self, context): 148 161 try: 149 162 template_name = self.template_name.resolve(context) 150 163 t = get_template(template_name) 151 return t. render(context)164 return t.stream(context) 152 165 except TemplateSyntaxError, e: 153 166 if settings.TEMPLATE_DEBUG: 154 167 raise 155 return ''168 return empty_string_generator() 156 169 except: 157 return ''# Fail silently for invalid included templates.170 return empty_string_generator() # Fail silently for invalid included templates. 158 171 159 172 def do_block(parser, token): 160 173 """ … … 214 227 if path[0] in ('"', "'") and path[-1] == path[0]: 215 228 return ConstantIncludeNode(path[1:-1]) 216 229 return IncludeNode(bits[1]) 230 231 def empty_string_generator(): 232 yield '' 217 233 218 234 register.tag('block', do_block) 219 235 register.tag('extends', do_extends) -
django/template/loader.py
174 174 get_template, or it may be a tuple to use select_template to find one of 175 175 the templates in the list. Returns a string. 176 176 """ 177 return ''.join(stream(template_name, dictionary, context_instance)) 178 179 def 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 """ 177 186 dictionary = dictionary or {} 178 187 if isinstance(template_name, (list, tuple)): 179 188 t = select_template(template_name) … … 183 192 context_instance.update(dictionary) 184 193 else: 185 194 context_instance = Context(dictionary) 186 return t. render(context_instance)195 return t.stream(context_instance) 187 196 188 197 def select_template(template_name_list): 189 198 "Given a list of template names, returns the first that can be loaded." -
docs/howto/custom-template-tags.txt
932 932 For more examples of complex rendering, see the source code for ``{% if %}``, 933 933 ``{% for %}``, ``{% ifequal %}`` and ``{% ifchanged %}``. They live in 934 934 ``django/template/defaulttags.py``. 935 936 Add streaming support 937 ~~~~~~~~~~~~~~~~~~~~~ 938 939 Simple tag like the earlier example of ``CurrentTimeNode`` does not need to 940 worry about streaming support. But if your custom tag is a more complex one 941 that have a "begin" tag and "end" tag like the above ``{% upper %}`` .. 942 ``{% endupper %}``, and you want the tag to be able to progressively render 943 its contents, you can define your own ``stream()`` method for the custom tag. 944 The ``stream()`` method takes the same arguments as ``render()`` but instead 945 of returning string, it needs to return string generator. 946 947 As an example, the ``stream()`` method for the ``{% upper %}`` tag above will 948 look like this:: 949 950 def stream(self, context): 951 for chunk in self.nodelist.stream(context): 952 yield chunk.upper() 953 954 If you define ``stream()`` in your custom template tag, you can define its 955 ``render()`` method as ``''.join(self.stream(context))`` to avoid duplicate 956 codes. -
docs/topics/http/shortcuts.txt
80 80 return HttpResponse(t.render(c), 81 81 mimetype="application/xhtml+xml") 82 82 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 83 107 ``redirect`` 84 108 ============ 85 109 -
docs/ref/templates/api.txt
207 207 self.database_record.delete() 208 208 sensitive_function.alters_data = True 209 209 210 Getting the rendering generator 211 ------------------------------- 212 213 As an alternative to rendering a context all-at-once, you can get the string 214 generator that render the context progressively by calling the ``Template`` 215 object's ``stream()`` method, passing the context as its argument. You can 216 then pass the generator to :class:`~django.http.HttpResponse` to get a 217 "streaming" effect on your view. 218 210 219 .. _invalid-template-variables: 211 220 212 221 How invalid variables are handled … … 690 699 calls ``render_to_string`` and feeds the result into an ``HttpResponse`` 691 700 suitable for returning directly from a view. 692 701 702 The ``stream()`` shortcut 703 ========================= 704 705 This shortcut is like :func:`render_to_string` but instead of returning 706 the whole rendered text of the template, this shortcut returns a generator 707 that successively renders parts of the template. See 708 :func:`~django.shortcuts.stream_to_response()` for an example of its usage. 709 693 710 Configuring the template system in standalone mode 694 711 ================================================== 695 712