Web UI for Python function - python

I have a python function that takes a series of integers as inputs and returns another series of integers. I'd like to distribute the function in the form of a web app.
The landing page should consist of a web form with a series of integer fields (with input validation), drop-down fields and a submit button. The submit button triggers the said python function and returns the results which should be rendered in an html table.
I am a complete novice with web development, but after some research it appears that flask is the most appropriate framework for me to use for the above task. My problem is that the documentation I have encountered so far deals primarily with blog development and therefore not particularly relevant for the type of app I am after.
I am therefore seeking any pointers (sample code, books, articles) or guidance to get me started with my task. In its simplest form, what I'm looking for is:
web form that takes one integer (1-10) and a second integer (1-5) from a drop down list
web form returns error if user enters invalid integer (<1, >10)
on submit button python function calculates the sum of the two integers
result is presented on the web form
All guidance appreciated.

Well it's quite simple really, it's all about how you present a form in html template, getting your view to get the form data, and passing the context back to the template.
I've quickly mocked a sample like what you want (nothing fancy, just back to the basic and show you how these work together), it's only a few lines of code in 2 files main.py (core file, like a view logic) and a template calculation.html:
main.py
from flask import Flask
from flask import render_template
from flask import request
app = Flask(__name__)
#app.route("/", methods=['GET', 'POST'])
def calculation():
result = 0
error = ''
# you may want to customize your GET... in this case not applicable
if request.method=='POST':
# get the form data
first = request.form['first']
second = request.form['second']
if first and second:
try:
# do your validation or logic here...
if int(first)>10 or int(first)<1:
raise ValueError
result = int(first) + int(second)
except ValueError:
# you may pass custom error message as you like
error = 'Please input integer from 1-10 only.'
# you render the template and pass the context result & error
return render_template('calculation.html', result=result, error=error)
if __name__ == "__main__":
app.run()
templates/calculation.html
<h1>Calculation</h1>
<form method="POST">
<input type="text" name="first" value="">
<select name="second">
<option value="1" selected>1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
<input type="submit" value="Submit">
{% if result %}
<p>
<label name='result'>Answer is: {{ result }}</label>
</p>
{% endif %}
{% if error %}
<p>
<label name="error">{{ error }}</label>
</p>
{% endif %}
</form>
Hopefully these are self explanatory and you can get to understand how to work with the basic of Flask and forms etc.
Read Flask Doc, and try to follow through, they are quite simple really and once you nail the basic, you may start looking for intermediate and advance topic.
FYI, there is an extension for WTForms called Flask-WTF, it is very handy when dealing with forms, although nothing stops you just doing everything in plain html form like above code.
Hope this helps and I wish you like the simplicity and flexiblity Flask brings you.

With NiceGUI you can build such UIs with a few lines of Python:
from nicegui import ui
i = ui.number(format='%d')
j = ui.select([1, 2, 3, 4, 5])
results = ui.table({
'columnDefs': [{'field': 'i'}, {'field': 'j'}, {'field': 'sum'}],
'rowData': [],
}).classes('max-h-40')
def calculate():
if not 1 <= i.value <= 10:
ui.notify('Invalid input')
return
results.options['rowData'].append({'i': i.value, 'j': j.value, 'sum': i.value + j.value})
results.update()
ui.button('Calculate', on_click=calculate)
ui.run()
HTML, JS and CSS are not required.
For this example I made use of ui.table, which builds on AG Grid and allows you to create pretty data tables. But you could display a simple ui.label as well.
(Disclaimer: I'm one of the developers of NiceGUI.)

Related

Flask and Retrieving data from HTML [duplicate]

This question already has answers here:
Sending data from HTML form to a Python script in Flask
(2 answers)
Closed 1 year ago.
i am relatively new to Flask/Python/HTML so please excuse my language.
Basically I am trying to retrieve the "name" field with request.form.get that was inputted in my HTML page. The name field was generated with a for loop in jinja. When i hit the checkbox, and click submit, i expect the request.form.get to retrieve the "name" field that was in that specific check boxes' data. However, when i test out the data, I get a 404 error that says NONE for the request.form.get value. I'm not sure where I am going wrong. I suspect it might be what I am plugging in as the name for request.form.get.
On my flask side:
#app.route("/recipedata", methods=["GET","POST"])
def recipedata():
if request.method == 'POST':
food = request.form.get("{{value.id}}")
On HTML side:
{% for value in foodinfo.results %}
<form action="/recipedata" method = "POST">
<input type="checkbox" name="{{value.id}}" value={{value.id}}>
<input type=text placeholder="{{value.id}}">
<input type="submit"> Go to Recipe info
</form>
{% endfor %}
The 2nd line in my form tag with type text was used to test whether my value.id was printing correctly in Jinja, and indeed it was. Additionally, for clarification, foodinfo is passed as a .json() object with nested dictionary key/values. Value.id allows me to access that dict's value at key 'id', I believe.
Thank you!
I don't think your function definition of recipedata() is valid as per python syntax. You need to indent code in python to preserve scope information. You can see more here.
Try with following function definition.
def recipedata():
if request.method == 'POST':
food = request.form.get("{{value.id}}")
I'm not sure if HTML part is causing any trouble.

validate_on_submit() fails when RadioButton choices are dynamically generated

I am creating quiz-like web application for learning languages using Flask, Jinja, WTForms, SqlAlchemy etc. Once an user completes such a language course by successfully going through all levels stored in JSON file I want the app offer him a practice mode, where the user will answer randomly selected levels.
When I run the app, I can see radio buttons generated with values from random level as I want, but when I choose any answer and submit it, form.validate_on_submit() returns False and form.errors returns {'practiceform': [u'Not a valid choice']}. When I hard-code value to currentLevel variable, it works properly.
views.py
#user_blueprint.route('/courses/<course>/quiz/practice',methods=['GET','POST'])
#login_required
def practice(course):
courseClass = class_for_name("project.models", course.capitalize())
courses = courseClass.query.filter_by(email=current_user.email).first()
maxLevel = courseClass.query.filter_by(email=current_user.email).first().get_maxLevel()
currentLevel = randint(0, maxLevel-1) # If this value is hard-coded or fetched from db, it works correctly
dic = generateQuestion(course, currentLevel)
display = dic["display"]
correct = dic["correct"]
options = dic["options"]
form = PracticeForm(request.form)
form.practiceform.choices = [(option, option) for option in options]
if form.validate_on_submit():
practiceForm = form.practiceform.data
if ((practiceForm == correct) and courses):
# Do something
flash("Nice job", 'success')
return redirect(url_for('user.practice', course=course))
else:
# Do something else
flash("Wrong answer", 'danger')
return redirect(url_for('user.practice', course=course))
return render_template('courses/practice.html', form=form, display=display)
forms.py
class PracticeForm(Form):
practiceform = RadioField('practice')
practice.html
{% extends "_base.html" %}
{% block content %}
<form action='' method='POST' role='form'>
<p>
<!-- Tried put form.csrf, form.csrf_token, form.hidden_tag() here -->
{{ form.practiceform() }}
</p>
<input type="submit" value="submit" />
</form>
{% endblock %}
So what am I missing there? What makes difference between lets say hardcoded level 25, which works properly or if the number 25 is randomly generated within randint?
My guess is that option is a int, bug WTForms get a str from request.form.
When data comes back from requests it is treated as a string by WTForms unless you specify a type explicitly with the coerce kwarg of the wtforms.fields.*Field constructor:
practiceform = RadioField('practice', coerce=int)
So I found that randint() caused the problem because the practice(course) method was called on both GET and POST actions which led to having two different integers -> two different forms most of the time. So I refactored the code. kept the practice(course) method for GET action and created a new method which handles POST action and this solved the problem.

Thwarting form double-submission through server side tokens (Django)

I am trying to implement a server-side check to prevent users from double-submitting my forms (Django web app).
One technique I'm trying is:
1) When the form is created, save a unique ID in the session, plus pass the unique ID value into the template as well.
2) When the form is submitted, pop the unique ID from the session, and compare it to the same unique ID retrieved from the form.
3) If the values are the same, allow processing, otherwise not.
These SO answers contributed in me formulating this.
Here's a quick look at my generalized code:
def my_view(request):
if request.method == 'POST':
secret_key_from_form = request.POST.get('sk','0')
secret_key_from_session = request.session.pop('secret_key','1')
if secret_key_from_form != secret_key_from_session:
return render(request,"404.html",{})
else:
# process the form normally
form = MyForm(request.POST,request.FILES)
if form.is_valid():
# do something
else:
# do something else
else:
f = MyForm()
secret_key = uuid.uuid4()
request.session["secret_key"] = secret_key
request.session.modified = True
return render(request,"my_form.html",{'form':f,'sk':secret_key})
And here's a sample template:
<form action="{% url 'my_view' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="sk" value="{{ sk }}">
{{ form.my_data }}
<button type="submit">OK</button>
</form>
This set up has failed to stop double-submission.
I.e., one can go on a click frenzy and still end up submitting tons of copies. Moreover, if I print secret_key_from_form and secret_key_from_session, I see them being printed multiple times, even though secret_key_from_session should have popped after the first attempt.
What doesn't this work? And how do I fix it?
UPDATE: when I use redis cache to save the value of the special key, this arrangement works perfectly. Therefore, it seems the culprit is me being unable to update request.session values (even with trying request.session.modified=True). I'm open to suggestions vis-a-vis what could be going wrong.
Note that this question specifically deals with a server-side solution to double-submission - my JS measures are separate and exclusive to this question.
You might just need request.session.modified = True. If you want to make sure that the session is deleting you can use del too.
secret_key_from_session = request.session.get('secret_key','1')
del request.session['secret_key']
request.session.modified = True
I couldn't figure out what caused the problem, but via substituting Redis cache for every request.session call, I was able to get my desired results. I'm open to suggestions.

Running Python script in Django from submit

Perhaps there is a different way of going about this problem, but I am fairly new to using Django.
I have written a custom Python script and would like to run a function or .py file when a user presses a "submit" button on the webpage.
How can I get a parameter to be passed into a Python function from a submit button using Django?
Typically what is done is you'd have your form submit a post request. You'd then intercept the request in your urls.py, where you'd call your function. So if your form looks like this:
<form action="submit" method="post">
<input type="text" name="info"><br>
<input type="submit" value="Submit">
</form>
your urls.py would have something like this:
url(r'^submit', views.submit)
and your views.py would have the function that would get the parameters that were passed through the post:
def submit(request):
info=request.POST['info']
# do something with info
This link gives a more in depth explanation.
After googling for a couple of days, i managed to piece together some solution on this question, which i needed for my project.
SwankSwashbucklers gave the general approach and i just wanted to add to it to complete the circle. This might not be the only solution so i am just giving one working example. So.. your template should contain the following code (as above with some extras):
your_template.html
{% extends base.html %}
{% block main_content %}
<form action="your_view_url" method="post">{% csrf_token %}
{{ form.as_table }}
// <input type="text" name="info_name" value="info_value">
<input type="submit" value="Submit">
</form>
<p> Post Data: {{ info }} </p>
<p> Result: {{ output }} </p>
{% endblock main_content %}
If you defined your form in forms.py and/or using your models for form rendering, then examine the rendered HTML to find out what was given to the "value" attributes rendered by Django in the form. "value" is what will be submitted in your POST request.
Your defined view will display the form, and also will process it once submited, so you will have 2 sections in it with an 'if' statement.
Django uses "GET" to open views, so the initial rendering display blank form
views.py
import subprocess
def your_view_name(request):
if request.method == 'GET':
form = your_form_name()
else:
if form.is_valid():
info = request.POST['info_name']
output = script_function(info)
// Here you are calling script_function,
// passing the POST data for 'info' to it;
return render(request, 'your_app/your_template.html', {
'info': info,
'output': output,
})
return render(request, 'your_app/your_template.html', {
'form': form,
})
def script_function( post_from_form )
print post_from_form //optional,check what the function received from the submit;
return subprocess.check_call(['/path/to/your/script.py', post_from_form])
forms.py
class your_form_name(forms.Form):
error_css_class = 'error' //custom css for form errors - ".error";
required_css_class = 'required' //custom css for required fields - ".required";
info_text = forms.CharField()
The "info_text" is what will be rendered in the template as in "input" field when you call form = your_form_name() . More on Django forms is here https://docs.djangoproject.com/en/1.9/ref/forms/fields/
When you press submit, the form will submit the data back to itself, so your view will pick that its a POST and will run is_valid , and then the value of output will be the error code returned by subprocess.check_call . If your script run OK, the value of "output" will be "0".
This is for "Django 1.4", and "Python 2.6". Latest versions have subprocess.check_output which can actually return the output from the script so you can render it back on the template.
Hope this helps:)
In django 1.11 and python 3.6, I had to use
return subprocess.run(['python', 'path_to_script//prog17.py', post_from_form], shell=False, timeout=1800)
The rest of #kossta's code worked fine.
What #SwankSwashbucklers says is the way to go.
If you also want to maintain your script separate from the view, you can also use a custom management command and use call_command to call it in the view. This way you can run the script from the command line as well with manage.py mycommand [myargument].
Except for the last part(calling the python script), #kosstas's code ran perfectly in my system. I am sharing what I had different. I am using Python 3.8 and Django 3.1. I had to use -
return subprocess.run(['python', 'windows_path_like_D:\\path_to_script\\prog17.py', post_from_form], shell=False, timeout=1800)
This code did not display the output in my system. To see the output, I had to put "stdout=PIPE" in place of "timeout=1800". That is:
output = run([sys.executable,'windows_path_like_D:\\path_to_script\\prog17.py', model_name], shell=False, stdout=PIPE)
For this at the top, I had to include:
from subprocess import run, PIPE
This was a great relief to me to see the output in my own eyes instantly!
python manage.py <your_script_name>
Here script name is ur python file .No need to mention .py extenstion.

post request with a form, then being able to save the data it returned on a separate request

let's say I have a form like this
<form role="form">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
</form>
then I have a fairly generic route that sorts through a static file
#app.route('/search/properties/', methods=['GET', 'POST'])
def properties_search():
form = request.form
if request.method == 'POST':
properties = CSVReader('properties.csv')
d = [x._asdict() for x in properties.data]
gen = stream_with_context(search_csv(d, form))
return Response(stream_with_context(
stream_template('advanced_search/results.html', form=form, rows=gen)
))
return render_template('advanced_search/advanced.html', form=form)
This will render a page with the results that I am looking for -- and all is well with that. However, on the page, I also want to add the capability to download the data as a CSV.
| download csv | | return to search |
- result 1
- result 2
...
The problem I am having is that, after the post request, I need some way to know what their last form request was on the application.
What is the best way to do this?
There isn't a single "best way" to do this, because what's "best" in your specific case might not suit another scenario. That being said, below are a couple of possible ways (which barely scratch the surface of what you need to do with them to make them production-ready) and there are many, many more.
You really need to research the options yourself here; the scenario you have is not specific to python or flask, it's pretty much a problem for anyone building an HTTP application that needs to remember state!
Render the received form values in hidden inputs
When you are rendering the initial search results. Render the "Download CSV" as the submit input of a form that will post these values back but to the download renderer.
I wouldn't be a huge fan of this as it requires a button and a POST when you are really looking for a GET and if you actually need the password in order to render the results, it's a security problem.
Whilst rendering the initial search results page, render the link for "Download CSV" such that it contains some kind of session ID.
When you receive the initial POST of the form to generate the search results. Take the email and store it in a database table (or some other persistent storage mechanism), recording some kind of generated ID as a result of this operation. You could also save any other search parameters the user submitted, etc.
This ID then becomes a querystring parameter of the link to "Download as CSV". i.e. when your template renders the initial search page, the link ends up like "http://blahblah.com/download?ID=12345"
When the link is requested by the user, lookup the search/ user information from the database based on the querystring ID parameter, and then pass it to whatever template will render the CSV for you.
There are many flavours of this approach and you need to pick the best for your scenario - you can save the search criteria for replay, or save the actual search results, it depends on the nature of the search, how expensive it is to run and whether "download as CSV" has to replay the search OR return the exact results originally obtained, etc), and you will also need to harden it. Don't send raw database IDs - send a hashed/encrypted version of them so that users cannot "guess" download IDs, etc.
I'd recommend this kind of approach because it doesn't require you to return the username/password to the client at all.
Hopefully that will get you thinking :)

Categories