Search code examples
pythonflaskjinja2yield

How to stream the output into browser using flask


I am new to flask. I have code which streams the output to browser. For my below code, i need to input the range value from browser and display the count till the range. thanks in advance.

import flask
import time

from jinja2 import Environment
from jinja2.loaders import FileSystemLoader

app = flask.Flask(__name__)

@app.route('/')
def index():
    def inner():
        for x in range(100):# eg: 100 should input value from browser 
            time.sleep(1)
            yield '%s<br/>\n' % x
    env = Environment(loader=FileSystemLoader('templates'))
    tmpl = env.get_template('result.html')
    return flask.Response(tmpl.generate(result=inner()))

app.run(debug=True)


result.html
<html>
{% block body %}
<body>
  {% for line in result %}
    {{ line }}
  {% endfor %}
</body>
{% endblock %}
</html>

Solution

  • Here is the answer to do what you are trying to do.

    I have modified your app code to:

    import flask
    import time
    
    from flask import request
    from jinja2 import Environment
    from jinja2.loaders import FileSystemLoader
    
    app = flask.Flask(__name__)
    
    @app.route('/', methods=['GET', 'POST'])
    def index():
        result = None
        if request.method == 'POST':
            counter = int(request.form.get('counter', 0))
            def inner():
                for x in range(counter):# eg: 100 should input value from browser 
                    time.sleep(1)
                    yield '%s<br/>\n' % x
            result = inner
        env = Environment(loader=FileSystemLoader('templates'))
        tmpl = env.get_template('result.html')
        return flask.Response(tmpl.generate(result=result if result is None else result()))
    
    app.run(debug=True)
    

    Here I have modified the index view to handle both GET and POST methods. If a simple GET requests comes it returns the template with result=None and if method is POST it returns=result()(I have assigned result=inner in POST block). The logic to handle None or a value is handled in the template.

    And your template needs to be changed to:

    <html>
    {% block body %}
    <body>
    {% if result%}
      {% for line in result %}
        {{ line }}
      {% endfor %}
    {% else %}
        <form method="post">
            <input type="text" placeholder="Enter a number" name="counter">
            <input type="submit" value="Submit">
        </form>
    {% endif %}
    </body>
    {% endblock %}
    </html>
    

    I have added the if-else condition as you are using the same template and even the URL doesn't change.

    You can follow Flask Quickstart Tutorial to learn more