Python Flask cannot get element from form - python

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.

Related

Flask Run Function on button click

I am looking to make a web page to enable triggering few processes on network. I have decided to use flask as the research i did online overwhelming suggest flask being better for beginners.
I have written the following code as a sample and to test my knowledge. I want to trigger a process on button click. However it doesn't seem to render the next page on click. Any help would be appreciated.
from flask import Flask, render_template
import testt
app = Flask(__name__)
#app.route('/',methods=['GET', 'POST'])
def hello_world():
return render_template('index.html')
#app.route('/my-link/',methods=['GET','POST'])
def my_link():
testt.trial()
work = 'working'
return render_template('process-complete.html',work= work)
if __name__ == '__main__':
app.run()
process-complete.html
{% extends 'base.html' %}
{% block head %}
<title>Task Master</title>
{% endblock %}
{% block body %}
<div class="content" method= "post">
<h1 style="text-align: center">Flask Trial</h1>
<div class="form">
<p> Process Completed {{work}}.<p>
</div>
</div>
{% endblock %}
The code below helped me execute the action i wanted to perform.
<p>Click Below to Start the Process<p>
<a class="button" href="{{ url_for('my_link') }}">Flask Trial</a>

Flask with Jinja issue with pagination after the first page

I have created a dropdown menu to search for parts by project number from an SQL database. The first page loads with the correct parts but any pages subsequent give the error of:
TypeError: show_compound() missing 1 required positional argument: 'search_string'
From what I've seen online it seems I may need to use *args or pass the search_string to the template but I am unsure of how to use *args or where to insert the search_string value on the template.
#parts_database.route('/searchcompound', methods=['GET', 'POST'])
#login_required
def compounds_search():
form = ProjectSearch(request.form)
if form.validate_on_submit():
search_string = form.select.data.project_number
return show_compound(search_string)
return render_template('parts_database/search_compounds.html', form=form)
#parts_database.route('/showcompound', methods=['GET'])
#login_required
def show_compound(search_string):
page = request.args.get('page', 1, type=int)
pagination = PartsTable.query.filter_by(project_number=search_string).order_by(PartsTable.part_number).paginate(page, per_page=15, error_out=False)
compound = pagination.items
page_10 = pagination.next_num+9
if page_10 > pagination.pages:
pageincrement = pagination.pages
else:
pageincrement = page_10
page_decrement = page - 10
if page_decrement < 1:
page_decrement = 1
return render_template('parts_database/showpartstable.html', compound=compound, pagination=pagination, pageincrement=pageincrement, page_decrement=page_decrement)
template :
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% import "_macros.html" as macros %}
{% block title %}Amos{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Parts</h1>
{% include 'parts_database/_showpartstable.html' %}
{% if pagination %}
<div class="pagination">
{{ macros.pagination_widget(page_decrement, pageincrement, pagination, '.show_compound') }}
</div>
{% endif %}
</div>
{% endblock %}
If a view takes an argument, you must include that variable in the route.
In your case, you are missing search_string in your show_compound route definition. Try something like this:
#parts_database.route('/showcompound/<search_string>', methods=['GET'])
#login_required
def show_compound(search_string):
(...)
EDIT:
Also, I'd sugest to redirect instead of calling another view's function.
Replace this:
if form.validate_on_submit():
search_string = form.select.data.project_number
return show_compound(search_string)
with this:
You'll have to import redirect before that:
from flask import redirect # (Add this at the top)
(...)
if form.validate_on_submit():
search_string = form.select.data.project_number
return redirect(url_for('parts_database.show_compound', search_string=search_string))

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>
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

Python Flask get item from HTML select 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 %}
The Problem is that your HTML form does not have a name.
request.args.get("som") needs an HTML form input with the name "som"
<select name="som" id="som" size="20">
Just change that line and add a name. The form is not interested in the id attribute.
You don't specified the method of the form, you have to do it! For example use this<form method="POST action"/pop">
Your form action is /pop. That means that if you submit the form it will do a POST request to the address /pop. Your code does only return a value for a GET request, therefore Flask complains you do not return anything. Write some code to process a POST request and return a text or rendered template.
BTW, in the code for GET you refer to request.args.get('som'); this gives you request arguments (i.e. in the URL), not from the form. som is in the form, so you cannot refer to it this way.

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)

Categories