How to stream the output into browser using flask - python

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>

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

Related

Importing from python to html in flask

I am using flask, and I am wondering, how can I get my application variable into my html template without needing to add it when calling the template like in this example:
render_template('home.html', app=app)
Maybe there is a import function I don't know about.
I would like to make it so I can loop through all of my endpoints(app.url_map.iter_rules()) and compare them with the current endpoint( request.endpoint) . My current main problem is getting all of the endpoints into my html file.
My current way of achieving my navigation bar is this:
<header>
<div class="head" align="center">
{% if request.endpoint!='users.login' and current_user.is_authenticated!=true %}
<a href="{{ url_for('users.login') }}">
<button class="tablecontent">Login</button>
</a>
{% endif %}
{% if request.endpoint!='users.register' %}
<a href="{{ url_for('users.register') }}">
<button class="tablecontent">Register</button>
</a>
{% endif %}
{% if request.endpoint!='core.home_page' %}
<a href="{{ url_for('core.home_page') }}">
<button class="tablecontent">Home</button>
</a>
{% endif %}
{% if current_user.is_authenticated %}
{% if request.endpoint!='users.logout' %}
<a href="{{ url_for('users.logout') }}">
<button class="tablecontent">Logout</button>
</a>
{% endif %}
{% endif %}
{% if current_user.is_authenticated %}
{% if request.endpoint!='products.new_product' %}
<a href="{{ url_for('products.add_product') }}">
<button class="tablecontent">Add Product</button>
</a>
{% endif %}
{% endif %}
</div>
</header>
The import error when I try to use the sitemap from the answer:
File "C:\Users\ulman\PycharmProjects\database_website_optimised\source\database_website\application.py", line 20, in register_applications
from database_website.applications.core.urls import blueprint as core_blueprint
File "C:\Users\ulman\PycharmProjects\database_website_optimised\source\database_website\applications\core\urls.py", line 3, in <module>
from database_website.applications.core import views
File "C:\Users\ulman\PycharmProjects\database_website_optimised\source\database_website\applications\core\views.py", line 7, in <module>
from database_website.properties import sitemap
File "C:\Users\ulman\PycharmProjects\database_website_optimised\source\database_website\properties.py", line 4, in <module>
from database_website.application import application
File "C:\Users\ulman\PycharmProjects\database_website_optimised\source\database_website\application.py", line 39, in <module>
application = Application.create()
File "C:\Users\ulman\PycharmProjects\database_website_optimised\source\database_website\application.py", line 35, in create
instance.register_applications()
File "C:\Users\ulman\PycharmProjects\database_website_optimised\source\database_website\application.py", line 20, in register_applications
from database_website.applications.core.urls import blueprint as core_blueprint
ImportError: cannot import name 'blueprint' from 'database_website.applications.core.urls' (C:\Users\ulman\PycharmProjects\database_website_optimised\source\database_website\applications\core\urls.py)
My view file:
from flask import Flask, render_template, url_for, flash, redirect, request
from flask.views import MethodView
from database_website.applications.views import FormViewMixin
from database_website.applications.products.models import Product
from database_website.applications.core import forms
from database_website.application import application
def has_no_empty_params(rule):
defaults = rule.defaults if rule.defaults is not None else ()
arguments = rule.arguments if rule.arguments is not None else ()
return len(defaults) >= len(arguments)
def sitemap():
links = []
for rule in application.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
return links
class HomePageView(MethodView, FormViewMixin):
def get(self):
form = forms.ProductSearchForm()
products = Product.query.all()
return render_template('core/home.html', title='Home', products=products, form=form)
def post(self):
product_search = request.form.get('search_name')
return redirect(url_for('products.search_product', search_name=product_search))
This is when it has sitemap hardoced, it still shows the exact same error because at the time, when the application object gets imported it does not exist yet. I can show the way I initialize my app.
Hopefully this will help :
def has_no_empty_params(rule):
defaults = rule.defaults if rule.defaults is not None else ()
arguments = rule.arguments if rule.arguments is not None else ()
return len(defaults) >= len(arguments)
def sitemap():
links = []
for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
return links
render_template('home.html', links=sitemap())
{% for url, endpoint in links %}
{{ endpoint }}
{% endfor %}

Flask : Storing , Get and Update List in session using flask_session

I have been trying for a couple of days, but I have had no success using a session to store the list( e.g., list of notes).
Below is the code that I have written. It store 2 variables successfully and when I try adding the third variable to the list , it overrides the second variable instead of appending to the list
from flask import Flask, render_template, request, session
from flask_session import Session
app = Flask(__name__)
app.config['SECRET_KEY'] = "some_random"
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_PERMANENT']= False
Session(app)
#app.route("/notes", methods=["GET","POST"])
def addNotes():
if 'notes' not in session:
session['notes'] = []
if request.method == "POST":
note=request.form.get("note")
notes_list = session['notes']
notes_list.append(note)
session['notes'] = notes_list
return render_template("notes.html", notes=session['notes'])
notes.html :
{% extends "layout.html" %}
{% block heading %}
Sticky Notes
{% endblock %}
{% block body %}
<ul>
{% for note in notes %}
<li>{{ note }}</li>
{% endfor %}
</ul>
<form action="{{ url_for('addNotes') }}" method="POST">
<input type="text" name="note" placeholder="Enter a note here">
<button>Add Note</button>
</form>
{% endblock %}
Please suggest if there is any way to store, get and update the list variable stored in the session object.
I have tried using session.modification=True also, which was based on some suggestions on stackoverflow.
#app.route("/notes", methods=["GET","POST"])
def addNotes():
if request.method == "POST":
note = request.form.get("note")
if 'notes' in session:
session['notes'].extend([note])
else:
session['notes'] = [note]
This works for me. But I mostly use sessions to store list of dicts.
Since you are storing your notes in the session, you don't have to pass it in the render template. You can directly access the session object in Jinja
<ul>
{% if session['notes'] %}
{% for note in session['notes'] %}
<li>{{ note }}</li>
{% endfor %}
{% endif %}
</ul>

Python Flask cannot get element from form

Im having trouble getting anything from the shown HTML form
I always get "ValueError: View function did not return a response"
Can somebody help me out here please? I have tried every variation of request.get that I can find on the web. Also if I specify my form should use post it uses get anyway - anybody know why this is?
Im new to flask so forgive my ignorance!
Thanks in advance.
The python file (routes.py)
from flask import Flask, render_template, request
import os
app = Flask(__name__)
musicpath = os.listdir(r"C:\Users\Oscar\Music\iTunes\iTunes Media\Music")
lsize = str(len(musicpath))
looper = len(musicpath)
#app.route('/')
def home():
return render_template('home.html', lsize=20, looper=looper, musicpath=musicpath)
#app.route('/pop', methods=['POST', 'GET'])
def pop():
if request.method == "GET":
text = request.args.get('som')
return text
#Have tried every variation of request.get
#app.route('/about')
def about():
name = "Hello!"
return render_template('about.html', name=name)
if __name__ == '__main__':
app.run(debug=True)
The html file (home.html)
{% extends "layout.html" %}
{% block content %}
<div class="jumbo">
<h2>A Music app!<h2>
</div>
<div>
{% if lsize %}
<form action="/pop">
<select id="som" size="20">
{% for i in range(looper):%}
<option value="{{i}}">{{ musicpath[i] }}</option>
{% endfor %}
</select>
</form>
{% endif %}
</div>
Select,
{% endblock %}
You don't have a name attribute on your select element. That is the attribute that browsers use to send information in forms; without it no data will be sent.
Note also that your pop handler does not do anything if the method is POST, even though you explicitly say you accept that method.

value error if cookie value is a string but not if an int

In my make_session function, if the passwords from the forms don't match some credentials, then it returns a string which ends up being stored as a cookie named session. In my index file, I have it set so on index.html, if the cookie value is {}, it asks for login information. If I change what is returned from the make_session function by replacing "invalid" with any integer, the code works as expected.
A bad solution to my problem is to add
except:
data = {}
before the return to my get_saved_data function setting data equal to {} but this ends up with the same results as if I had no cookie in my browser at all, but it gets rid of my error which is here: https://gist.github.com/anonymous/e101aa46f154a075b038
I suspect that the get_saved_data function may be a fault.
Map of my directory:
|---- layout.html
|---- index.html
|--- templates -|
Project -|
|--- test.py
test.py:
from flask import Flask, render_template, redirect, url_for, request, make_response
import json
def get_saved_data(key):
try:
data = json.loads(request.cookies.get(key))
except TypeError:
data = {}
return data
def make_session(form_data):
if form_data.get('username') == "username" and form_data.get('password') == "password":
return "12345"
else:
return "invalid"
app = Flask(__name__)
#app.route('/')
def index():
data = get_saved_data("session")
return render_template('index.html', saves=data)
#app.route('/login', methods=['POST'])
def login():
response = make_response(redirect(url_for('index')))
response.set_cookie("session", make_session(dict(request.form.items())))
return response
app.run(debug=True, host='0.0.0.0', port=8000)
index.html:
{% extends "layout.html" %}
{% block content %}
{% if saves == {}: %}
<p>Please log in.</p>
{% else: %}
<p>Your Session value is: {{ saves }}</p>
{% endif %}
{% if saves == {}: %}
<form action="{{ url_for('login') }}" method="POST">
<p>We take your private information very seriously. All data is encrypted not once but twice! in ROT13 to provide the best security.</p><br />
<label for="username">Please enter your username:</label>
<input type="text" name="username" /><br />
<label for="password">Please enter your password:</label>
<input type="text" name="password" /><br />
<button class="btn">Log In</button>
</form>
{% endif %}
{% endblock %}
layout.html:
<!DOCTYPE html>
<html>
<head>
<title>Character Generator</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
Change your get_saved_data to the following will make any difference?
def get_saved_data(key):
data = json.loads(request.cookies.get(key, '{}'))
return data
return '{}' if there is no such key, make sure json won't complain anything.
While I haven't found a way to directly fix the issue, and using lord63.j's answer saved me a few lines of code, a way to indirectly fix the issue is to save the cookie value as a dictionary by modifying the login function to this:
#app.route('/login', methods=['POST'])
def login():
response = make_response(redirect(url_for('index')))
response.set_cookie("data", json.dumps({"session": make_session(dict(request.form.items()))}))
return response
from them on, in order to access the session value I'll need to use
saves.get('session')
as saves is equivalent to
json.loads(request.cookies.get('data', '{}'))
in the index function
#app.route('/')
def index():
data = get_saved_data("data")
return render_template('index.html', saves=data)

How to move flask wtform from extending view to extended view and instantiate forms for all views?

To make my question clearer, here is a little application that takes a sentence input and outputs that sentence twice.
I have base.html:
<html>
<head>
<title> My site </title>
<body>
{% block content %}{% endblock %}
</body>
</html>
and index.html:
{% extends "base.html" %}
{% block content %}
{{ s }}
<form action="" method="post" name="blah">
{{ form.hidden_tag() }}
{{ form.sentence(size=80) }}
<input type="submit" value="Doubler"></p>
</form>
{% endblock %}
Here is part of views.py:
from forms import DoublerForm
#app.route('/index')
def index():
form = DoublerForm()
if form.validate_on_submit():
s = form.sentence.data
return render_template('index.html', form=form, s=str(s) + str(s))
return render_template('index.html', form=form, s="")
And here is forms.py, without all the imports:
class DoublerForm(Form):
sentence = StringField(u'Text')
This seems to work OK. But what I would like is to have my input form in the base.html template so that this shows up on all pages that extend it, not just the index page. How can I move the form to the base.html and instantiate the form for all views that extend base.html?
You can use the flask.g object and flask.before_request.
from flask import Flask, render_template, g
from flask_wtf import Form
from wtforms import StringField
#app.before_request
def get_default_context():
"""
helper function that returns a default context used by render_template
"""
g.doubler_form = DoublerForm()
g.example_string = "example =D"
#app.route('/', methods=["GET", "POST"])
def index():
form = g.get("doubler_form")
if form.validate_on_submit():
s = form.sentence.data
return render_template('index.html', form=form, s=str(s) + str(s))
return render_template('index.html', form=form, s="")
You can also explicitly define a context function
def get_default_context():
"""
helper function that returns a default context used by render_template
"""
context = {}
context["doubler_form"] = form = DoublerForm()
context["example_string"] = "example =D"
return context
and is used like this
#app.route('/faq/', methods=['GET'])
def faq_page():
"""
returns a static page that answers the most common questions found in limbo
"""
context = controllers.get_default_context()
return render_template('faq.html', **context)
Now, you'll have whatever objects you add to the context dictionary available in all templates that unpack the context dictionary.
index.html
{% extends "base.html" %}
{% block content %}
{{ s }}
{% endblock %}
base.html
<html>
<head>
<title> My site </title>
<body>
{% block content %}{% endblock %}
<form action="" method="post" name="blah">
{{ doubler_form.hidden_tag() }}
{{ doubler_form.sentence(size=80) }}
{{ example_string }}
<input type="submit" value="Doubler"></p>
</form>
</body>
</html>

Categories