switch/toogle results in BadRequestKeyError: 400 Bad Request - python

I have an index.html generated with python & flask that shows a list. I want to filter this list by a bootstrap switch. Now I have the following code and it works to show my index.html list by GET request. enabling the switch also works and shows the filtered list. But when switching back to unfiltered/off I receive a bad request. the problem is with switchvalue = request.form['switch'] but I dont understand why this is.
Python code:
#app.route('/', methods=['POST', 'GET'])
def index():
conn = get_db_connection()
if request.method == 'POST':
switchvalue = request.form['switch']
flash(switchvalue)
if switchvalue == '1':
rows = conn.execute('SELECT Name, CAST (Points AS int) as Points, isActive FROM table WHERE isActive = "Active"').fetchall()
conn.close()
return render_template('index.html', rows=rows, switchcheck=1)
rows = conn.execute('SELECT Name, CAST (Points AS int) as Points, isActive FROM table').fetchall()
conn.close()
return render_template('index.html', rows=rows, switchcheck=0)
HTML:
...
{% block content %}
<h1>{% block title %} Title {% endblock %}</h1>
<form method="POST" action="{{ url_for('index') }}">
<div class="custom-control custom-switch">
{% if switchcheck == 0 %}
<input type="checkbox" name="switch" onclick=this.form.submit() value="1" class="custom-control-input" id="customSwitch1">
{% else %}
<input type="checkbox" name="switch" onclick=this.form.submit() value="0" class="custom-control-input" id="customSwitch1" checked>
{% endif %}
<label class="custom-control-label" for="customSwitch1">Active</label>
</div>
</form>
{% for row in rows %}
...

So it seems like the value of switch does not get POSTed because switches are handled as checkboxes and when they are not checked they dont submit value. therefore submit.form(switch) doesnt see data.
I found my hint here: How to utilize Bootstrap Ti Ta Toggle Checkbox with Flask
Also there is mentioned to use hidden form with name=switch to handle this problem but was no success for me. My workaround looks as follows:
try:
switchvalue = request.form['vrswitch']
except:
switchvalue = 0
I bet there are more elegant ways to do this but it works!

Related

How can i populate a form from a Dropdown Control Selector using SQL and Flask?

I'm stuck trying to populate a HTML form, selecting the company from a dropdown, similar result to this, in SQL and Flask.
I have no clue which way to go, to bring a value from <option> in HTML back to Flask file.
Here are some samples of my code.
Flask Sample:
#app.route("/profile" , methods=['GET', 'POST'])
def profile():
if request.method == "GET":
querieCompanies = db.execute("SELECT name FROM companies ORDER BY name ASC")
querieAll = db.execute("SELECT * FROM companies WHERE id = ?", ??WHATGOESHERE??).fetchall()
return render_template("profile.html", querieCompanies=querieCompanies, querieAll=querieAll)
HTML Sample:
{% extends "layout.html" %}
{% block main %}
<form name="listprofile" method="get">
<div class="mb-3">
<h5>Select company</h5>
<select class="mx-auto w-auto" name="dropdownmenu">
<option disabled selected>Companies</option>
{% for companies in querieCompanies %}
<option value="{{ companies[0] }}">{{ companies[0] }}</option>
{% endfor %}
</select>
</div>
<div class="container">
<div class="row">
<div class="col mb-3 p-1 d-inline-bloc">
<h5>Name</h5>
{% for data in querieAll %}
??WHAT GOES HERE??
{% endfor %}
Also, it seems i can't populate <input>, i'm able to populate a <textbox>, is there another solution?
Use request.args for getting data from get-method form
if you using sqlalchemy, then use colon-params in db.execute()
from flask import request
querieAll = db.execute("SELECT * FROM companies WHERE id = :value", {
'value': request.args.get('dropdownmenu')
}).fetchall()
In template for-loop, data is a tuple with companies columns data.

Create a preview screen in Django

I have a Django form that receives a text (that I copy from Google Classroom: a bunch of student comments). I use these comments to make student's attendance. What I want to achieve is:
Accessing /insertion/ url via GET user receive the page form as a response, to choose the class (class01, class02, etc) and to past the text
When the user clicks on submit in this form (post method), it is redirect to the same /insertion/ url, but now the form is bound to the data submited, and the page shows a preview page (based on a boolean variable I'm passing through context), showing what students are present and what are absent based on the text informed. At that page, a new submit button will be shown below a text like "if everything's ok, hit the ok button".
After click this ok button, a pdf will be generated and the user will be redirected to /files/ url, to see the generated pdf and previous generated pdf.
views.py
def insertion(request):
context = {}
if request.method == 'GET':
form = AttendanceDataForm()
context.update({"form": form})
if request.method == 'POST':
form = AttendanceDataForm(request.POST)
context.update({"form": form})
if form.is_valid():
lesson = form.cleaned_data['lesson']
raw_text = form.cleaned_data['raw_text']
# Get course students
course_students = md.Student.objects.filter(course_id=lesson.course_id)
# Get present students based on raw text informed
present_students = [s for s in course_students if s.full_name in raw_text]
# Get absent students based on raw text informed
absent_students = [s for s in course_students if s.full_name not in raw_text]
context.update({
"present_students": present_students,
"absent_students": absent_students,
"render_preview": True
})
context.update({"active_freq": True})
return render(request, 'core/insertion.html', context)
def files(request):
context = {}
if request.method == 'POST':
# How can I access all expensive calculation I did in the previous view?
context.update({"active_gen": True})
return render(request, "core/files.html", context)
insertion.html
<div class="row">
<div class="col-12 col-md-6">
<h3>Informar FrequĂȘncia</h3>
{% crispy form %}
</div>
<div class="col-12 col-md-6">
{% if render_preview %}
<div class="container">
<div class="row p-4 bg-white rounded mt-4">
<div class="col-12 col-sm-6">
<h5>Alunos presentes</h5>
<ul class="previewer-list">
{% for student in present_students %}
<li>{{ student.id }} - {{ student.full_name }}</li>
{% endfor %}
</ul>
</div>
<div class="col-12 col-sm-6">
<h5>Alunos ausentes</h5>
<ul class="previewer-list">
{% for student in absent_students %}
<li>{{ student.id }} - {{ student.full_name }}</li>
{% endfor %}
</ul>
</div>
</div>
<p class="mt-3">If everything's ok, hit the OK button</p>
<form method="post" action="{% url "core:files" %}">
{% csrf_token %}
<button type="submit" class="btn btn-primary">OK</button>
</form>
</div>
{% endif %}
</div>
</div>
I could get to implement 1 and 2, but 3 is a mistery right now. What I couldn't get is how I can access the expensive calculations I did in insertion view in the files view. How can I do that?
Here's a solution using session framework.
We'll save the calculations in the session and access those values in another view later.
For starters, we'll just save the ids (pk) of the students instead of the student instances because they are not JSON serializable [See note below].
def insertion(request):
# do expensive calucations ...
present_ids = [s.pk for s in present_students]
absent_ids = [s.pk for s in absent_students]
request.session['attendance_data'] = {
'present_ids': present_ids,
'absent_ids': absent_ids
}
def files(request):
attendance_data = request.session.get('attendance_data')
if not attendance_data:
# show error or something else ...
pass
present_students = md.Student.objects.filter(
pk__in=attendance_data['present_ids']
)
absent_students = md.Student.objects.filter(
pk__in=attendance_data['absent_ids']
)
# generate the pdf ...
Note: If you wish, you can also save the student instances in the session but you'll have to change the SESSION_SERIALIZER setting to use the PickleSerializer. See notes about session serialization.
You could submit the primary keys as form data in hidden fields. Just choose an appropriate delimiter based on your primary key (for example, don't delimit with a hyphen if you use a GUID primary key).
<form method="post" action="{% url "core:files" %}">
{% csrf_token %}
<input type="hidden"
name="present"
value="{% for s in present_students %}{{ s.pk }},{% endfor %}"
>
<input type="hidden"
name="absent"
value="{% for s in absent_students %}{{ s.pk }},{% endfor %}"
>
<button type="submit" class="btn btn-primary">OK</button>
</form>
Then in the view you can pick up the PKs in the view from the form data then request.
def files(request):
context = {}
if request.method == 'POST':
present_pks = request.POST.pop('present').split(',')[:-1]
absent_pks = request.POST.pop('absent').split(',')[:-1]
# do type conversions if needed
...
# Because we already have the pks separated, we can combine them
# for the query in order to do just 1 query
course_students = md.Student.objects.filter(pk__in=present_pks + absent_pks).all()
absent_students = []
present_students = []
for student in course_students:
if student.pk in absent_pks:
absent_students.append(student)
else:
present_students.append(student)

Having a select option stay selected after POST with Flask

I'm trying to get a select option that is selected to stay after the page refresh using Flask. I have attempted to do so with Jinga2, but it's is not working:
<div class="col-sm-4 col-lg-4 col-md-4">
<select class="form-control" id="myselect" name="thing" required>
<option value="" {% if thing=='' %} selected {% endif %} ></option>
<option value="Foo" name="Foo" id="Foo" {% if thing =="Foo" %} selected {% endif %}>Foo</option>
<option value="Bar" name="Bar" id="Bar" {% if thing =="Bar" %} selected {% endif %}>Bar</option>
</select>
</div>
Where the variable energy is populated and passed through with Python. After looking into this, I feel that this is the way to make this work in Flask, though apparently not. Any assistance would be appreciated!
#app,route('/things', methods=['POST']
def things()
if len(facts['thing']) > 11:
energy = [facts['thing'][0:8],facts['thing'][9:]]
else:
energy = [facts['things']]
...
return render_template('thing.html', thing=energy)
Please see this example as it works for what you're trying to do. I can't exactly debug what's going wrong in your code because you've provided me with parts and I don't know what they're doing.
Folder structure
Test
|___templates
| |___things.html
|___Test.py
things.html
<form method="post">
<div class="col-sm-4 col-lg-4 col-md-4">
<select title="thing" class="form-control" id="myselect" name="thing" required>
<option value="" {% if thing=='' %} selected {% endif %} ></option>
<option value="Foo" name="Foo" id="Foo" {% if thing =="Foo" %} selected {% endif %} >Foo</option>
<option value="Bar" name="Bar" id="Bar" {% if thing =='Bar' %} selected {% endif %}>Bar</option>
</select>
<button type="submit">SEND</button>
</div>
</form>
Test.py
from flask import Flask, render_template, request
app = Flask(__name__)
PORT = 5000
#app.route('/things', methods=['GET', 'POST'])
def things():
"""
Accepts both GET and POST requests. If it's a GET request,
you wouldn't have a last selected thing, so it's set to an
empty string. If it's a POST request, we fetch the selected
thing and return the same template with the pre-selected
thing.
You can improve on this and save the last selected thing
into the session data and attempt to retrieve it from there.
"""
thing = ''
if request.method == 'GET':
return render_template('things.html', thing=thing)
else:
thing = request.form.get('thing', '')
return render_template('things.html', thing=thing)
if __name__ == '__main__':
app.run(port=PORT)
The important part is rendering the page with selected in your desired option:
<option value="Foo" selected>Foo</option>
double_j's answer to use templates to insert it into your html works great, but if you're building the dropdown from a list it may be easier to build your html from python:
import flask
import socket
app = flask.Flask(__name__)
all_things = ['Foo', 'Bar', 'Fizz', 'Buzz']
current_thing = None
def show_selection(target):
if target == current_thing:
return 'selected'
else:
return ''
def menu():
template = '''<form action = "things" method = "post">
<select id="target" name="target">
{targets}
</select>
<input type="submit" name="build" value="Build">
</form>
'''
# vvv This is the important part vvv
targets = [f'<option value="{t}" {show_selection(t)}>{t}</option>' for t in all_things]
return template.format(
targets='\n'.join(targets)
)
# ^^^ This is the important part ^^^
#app.route('/things', methods=['GET', 'POST'])
def things():
global current_thing
current_thing = flask.request.form.get('target')
page = menu()
if flask.request.method == 'POST' and 'build' in flask.request.form:
page += f'Building {current_thing}'
else:
page += 'Press Build button'
return page
if __name__ == '__main__':
PORT = 8080
print("Visit http://{}:{} to trigger builds".format(socket.gethostname(), PORT))
app.run(host='0.0.0.0', port=PORT, debug=True)
try this, if you have not already
{% if thing == "Foo" %}
<option value = "Foo" name ="Foo" id="Foo" selected>Foo</option>
<option value = "Bar" name ="Bar" id="Bar">Bar</option>
{% elif thing == "Bar" %}
<option value = "Foo" name ="Foo" id="Foo">Foo</option>
<option value = "Bar" name ="Bar" id="Bar" selected>Bar</option>
{% endif %}

Search function in Flask

I have a problem with my search. At the moment i am trying to write a small receipe portal and i am trying to search words in tables user,category and recipe. When i write some word, i receive an error message:
Bad request. The browser (or proxy) sent a request that this server
could not understand.
I suppose, that problem stays in my function search, but i dont see it.
#app.route("/search", methods=['GET', 'POST'])
def search():
cursor = g.con.cursor()
cursor.execute('SELECT * FROM nutzer, kategorien, rezepte WHERE Nutzername OR Titel = %s', (request.form["search"],))
result = cursor.fetchall()
cursor.close()
return render_template('Results.html', result = result)
{% extends "layout.html" %}
{% block body %}
<table border="1">
{% for i in result %}
<tr><td>{{ i.1 }}</td></tr>
{% endfor %}
</table>
{% endblock %}
HTML Code of the searchbar
<form action="search">
<input name="search" type="text" placeholder="suchen" value="{{ request.form.search}}" required />
<button>finden</button>
</form>
request.form() implies the POST method, while the default one is GET. You should either check request.method and use request.args() in the case of GET, or add the argument method="POST" to the <form> (and leave POST as the only accepted method in #app.route().
I think your form action has to point to your search endpoint.
<form action="{{ url_for(search) }}">
<input name="search" type="text" placeholder="suchen" value="" required />
<button>finden</button>
</form>

Submitting multiple forms in Django

I'm not sure if i'm going about this completely the wrong way, but in my html template i have a for loop that i want to present multiple forms, and one submit button to submit the data from all forms:
{% for i in Attribute_list %}
<form action="/Project/create/" method=post>{% csrf_token %}
{{ i }}:
<input type=text name={{ i }}><br>
<hr>
{% endfor %}
<input type=submit>
The problem with this is it only submits the last form.
The other problem i'm running into is getting the data back from the view. Since i'm naming the form the variable "i", i don't know how to "get" this data in my views.py:
def create_config(request):
if request.method == 'POST':
data_list = []
for data in request.POST.getlist():
data_list.append(data)
can You check this?
<form action="/Project/create/" method="post">
{% csrf_token %}
{% for i in Attribute_list %}
{{ i }}: <input type="text" name="{{ i }}"><br>
<hr>
{% endfor %}
<input type="submit">
</form>
As I understand without JS regardless how many forms You create only one POST request will be made.
In oyur example HTML is not valid so It can behave different ways in different browsers. But as soon as You have not closed form last one should be submitted.
As for second part
def create_config(request):
if request.method == 'POST':
data_list = []
for data in request.POST.getlist():
data_list.append(data)
I think You should use your Attribute_list. Or You can just iterate over all `POST' variables obtained.
def create_config(request):
if request.method == 'POST':
data_list = []
for key in request.POST:
data_list.append(request.POST[key]) # or .extend(request.POST.getlist(key)

Categories