| 
									
										
										
										
											2011-07-28 04:34:18 +08:00
										 |  |  | Streaming Contents
 | 
					
						
							|  |  |  | ==================
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Sometimes you want to send an enormous amount of data to the client, much
 | 
					
						
							|  |  |  | more than you want to keep in memory.  When you are generating the data on
 | 
					
						
							|  |  |  | the fly though, how do you send that back to the client without the
 | 
					
						
							|  |  |  | roundtrip to the filesystem?
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The answer is by using generators and direct responses.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Basic Usage
 | 
					
						
							|  |  |  | -----------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This is a basic view function that generates a lot of CSV data on the fly.
 | 
					
						
							|  |  |  | The trick is to have an inner function that uses a generator to generate
 | 
					
						
							| 
									
										
										
										
											2011-09-20 06:03:24 +08:00
										 |  |  | data and to then invoke that function and pass it to a response object::
 | 
					
						
							| 
									
										
										
										
											2011-07-28 04:34:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     from flask import Response
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route('/large.csv')
 | 
					
						
							|  |  |  |     def generate_large_csv():
 | 
					
						
							|  |  |  |         def generate():
 | 
					
						
							|  |  |  |             for row in iter_all_rows():
 | 
					
						
							|  |  |  |                 yield ','.join(row) + '\n'
 | 
					
						
							| 
									
										
										
										
											2011-09-20 06:03:24 +08:00
										 |  |  |         return Response(generate(), mimetype='text/csv')
 | 
					
						
							| 
									
										
										
										
											2011-07-28 04:34:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | Each ``yield`` expression is directly sent to the browser.  Now though
 | 
					
						
							|  |  |  | that some WSGI middlewares might break streaming, so be careful there in
 | 
					
						
							|  |  |  | debug environments with profilers and other things you might have enabled.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Streaming from Templates
 | 
					
						
							|  |  |  | ------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The Jinja2 template engine also supports rendering templates piece by
 | 
					
						
							|  |  |  | piece.  This functionality is not directly exposed by Flask because it is
 | 
					
						
							| 
									
										
										
										
											2011-07-28 04:34:56 +08:00
										 |  |  | quite uncommon, but you can easily do it yourself::
 | 
					
						
							| 
									
										
										
										
											2011-07-28 04:34:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     from flask import Response
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def stream_template(template_name, **context):
 | 
					
						
							|  |  |  |         app.update_template_context(context)
 | 
					
						
							|  |  |  |         t = app.jinja_env.get_template(template_name)
 | 
					
						
							|  |  |  |         rv = t.stream(context)
 | 
					
						
							|  |  |  |         rv.enable_buffering(5)
 | 
					
						
							|  |  |  |         return rv
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route('/my-large-page.html')
 | 
					
						
							|  |  |  |     def render_large_template():
 | 
					
						
							|  |  |  |         rows = iter_all_rows()
 | 
					
						
							| 
									
										
										
										
											2011-09-20 06:03:24 +08:00
										 |  |  |         return Response(stream_template('the_template.html', rows=rows))
 | 
					
						
							| 
									
										
										
										
											2011-07-28 04:34:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | The trick here is to get the template object from the Jinja2 environment
 | 
					
						
							|  |  |  | on the application and to call :meth:`~jinja2.Template.stream` instead of
 | 
					
						
							|  |  |  | :meth:`~jinja2.Template.render` which returns a stream object instead of a
 | 
					
						
							|  |  |  | string.  Since we're bypassing the Flask template render functions and
 | 
					
						
							|  |  |  | using the template object itself we have to make sure to update the render
 | 
					
						
							|  |  |  | context ourselves by calling :meth:`~flask.Flask.update_template_context`.
 | 
					
						
							|  |  |  | The template is then evaluated as the stream is iterated over.  Since each
 | 
					
						
							|  |  |  | time you do a yield the server will flush the content to the client you
 | 
					
						
							|  |  |  | might want to buffer up a few items in the template which you can do with
 | 
					
						
							|  |  |  | ``rv.enable_buffering(size)``.  ``5`` is a sane default.
 |