400 Bad Request python flask - python

Hi I'm new to python flask, i have what seems a simple issue of a 400 bad request error, that should be due to having wrongly named variables in form when trying to pass them to a function in python flask. I have done some research but i still can't figure out where i go wrong with this code, any help would be really appreciated.
Here is the code for the html form
<html>
<body>
<h1>Add a Munroe to your list</h1>
<form action = "{{ url_for('addmunro') }}" method="POST"
enctype = "multipart/form-data">
Name<br>
<input type="text" name="mnName"/><br>
Description<br>
<input type="text" name="mnDesc"/><br>
Region<br>
<input type="text" name="mnRegion"/><br>
Height<br>
<input type="text" name="mnHeight"/><br>
Walk date<br>
<input type="text" name="mnDate"/><br>
Walk image<br>
<input type="text" name="mnImage"/><br>
<br>
<br>
<input type="submit" name="add-munro.html" value = "ADD MUNRO"/>
</form>
</body>
</html>
And here is the code for the python flask application
from flask import Flask, render_template, url_for, redirect, json, request
app = Flask(__name__)
#app.route('/add-munro.html', methods=['GET'])
def addmunro():
#Create an empty list
mnList={}
#Create a munro dictionary
munro = {'name':request.form['mnName'],
'desc':request.form['mnDesc'],
'region':request.form['mnRegion'],
'height':request.form['mnHeight'],
'date':request.form['mnDate'],
'image':request.form['mnImage']}
#the munro dictionary is added to mnList
#mnList.append(munro)
return render_template('add-munro.html')
if __name__ == "__main__":
app.run(host = '0.0.0.0', debug = True)

There are several mistakes:
You are making POST request but handling only GET request
.html is not needed in routing
munro object is not passed to the template
I have updated these and now it's good to go:
application.py
from flask import Flask, render_template, request, url_for
app = Flask(__name__)
#app.route('/add-munro', methods=['GET','POST'])
def addmunro():
if request.method == "POST":
#Create an empty list
mnList={}
#Create a munro dictionary
munro = {'name':request.form['mnName'],
'desc':request.form['mnDesc'],
'region':request.form['mnRegion'],
'height':request.form['mnHeight'],
'date':request.form['mnDate'],
'image':request.form['mnImage']}
return render_template('add-munro.html', munro=munro)
else:
return render_template('add-munro.html')
if __name__ == '__main__':
app.run(debug=True)
add-munro.html
<html>
<body>
{% if munro is defined -%}
<h3>
Name: {{ munro.name }}
</h3>
<h3>
Description: {{ munro.desc }}
</h3>
<h3>
Region: {{ munro.region }}
</h3>
<h3>
Height: {{ munro.height }}
</h3>
{%- endif %}
<h1>Add a Munroe to your list</h1>
<form action = "{{ url_for('addmunro') }}" method="POST"
enctype = "multipart/form-data">
Name<br>
<input type="text" name="mnName"/><br>
Description<br>
<input type="text" name="mnDesc"/><br>
Region<br>
<input type="text" name="mnRegion"/><br>
Height<br>
<input type="text" name="mnHeight"/><br>
Walk date<br>
<input type="text" name="mnDate"/><br>
Walk image<br>
<input type="text" name="mnImage"/><br>
<br>
<br>
<input type="submit" value = "ADD MUNRO"/>
</form>
</body>
</html>
Output:

You are making a POST request in your form, but only allow the GET method in your app route. Change #app.route('/add-munro.html', methods=['GET']) to #app.route('/add-munro.html', methods=['POST']).

Related

HTML form (python, flask): have a button submit a unique parameter to method

I have a good HTML form right now that calls a method via a button.
What I want to do is create several new buttons that call the same method, but each pass a different parameter.
The specific use case is: my button right now retrieves a random album with get_random_album().
I want to make a "Jazz", "Classical", and "Rock" buttons that respectively call get_random_album(genre=<genre>).
MVE:
from flask import Flask,redirect
# Below import commented out for MVE
# from discogs import get_random_album
# TODO: look at more advanced form builders
# Create app instance
app = Flask(__name__)
# Create homepage with button
#app.route("/")
def home():
return """
<html><body>
<h2> Spin yo records </h2>
<form action="/query">
<input type='submit' value="I'm Feeling Lucky">
</body></html>
"""
# Backend query to discogs
#app.route("/query")
def link():
# return redirect(get_random_album())
# Real implementation commented out
# Scratch implementation here
def get_random_album(genre=None):
url = "https://www.google.com"
if genre == 'Jazz':
return url + "/search?q=jazz"
if genre == 'Classical':
return url + "/search?q=classical"
if genre == 'Rock':
return url + "/search?q=rock"
return url
return redirect(get_random_album())
# Run app
if __name__ == "__main__":
app.run(debug=True,port=600)
Actual project
First create buttons with the same name but with different value
#app.route("/")
def home():
return """
<html>
<body>
<h2> Spin yo records </h2>
<form action="/query">
<input type="submit" name="selected_genre" value="Jazz">
<input type="submit" name="selected_genre" value="Classic">
<input type="submit" name="selected_genre" value="Rock">
<input type="submit" name="selected_genre" value="I'm Feeling Lucky">
</form>
</body>
</html>
"""
And next you can get selected value using request and name used in buttons
from flask import request
import random
#app.route("/query")
def link():
allowed_values = ('Jazz', 'Classic', 'Rock')
genre = request.args.get("selected_genre")
# "I'm Feeling Lucky"
if genre not in allowed_values:
genre = random.choice(allowed_values)
genre = genre.lower()
url = f"https://www.google.com/search?q={genre}"
return redirect(url)
Full example
from flask import Flask, redirect, request
import random
app = Flask(__name__)
#app.route("/")
def home():
return """
<html>
<body>
<h2> Spin yo records </h2>
<form action="/query">
<input type="submit" name="selected_genre" value="Jazz">
<input type="submit" name="selected_genre" value="Classic">
<input type="submit" name="selected_genre" value="Rock">
<input type="submit" name="selected_genre" value="I'm Feeling Lucky">
</form>
</body>
</html>
"""
#app.route("/query")
def link():
allowed_values = ('Jazz', 'Classic', 'Rock')
genre = request.args.get("selected_genre")
if genre not in allowed_values:
genre = random.choice(allowed_values)
genre = genre.lower()
url = f"https://www.google.com/search?q={genre}"
return redirect(url)
if __name__ == "__main__":
app.run(debug=True,port=600)
In previous version it sends value in url ie. /query?selected_genre=Rock - so everyone can see it or easily it can try to use own value. And this is why I used allowed_values to block it.
To hide selected genre from url you have to use:
<form ... method="POST">
#app.route(..., methods=['GET', 'POST']) (or methods=['POST'])
request.form instead of request.args
Full example
from flask import Flask, redirect, request
import random
app = Flask(__name__)
#app.route("/")
def home():
return """
<html>
<body>
<h2> Spin yo records </h2>
<form action="/query" method="POST">
<input type="submit" name="selected_genre" value="Jazz">
<input type="submit" name="selected_genre" value="Classic">
<input type="submit" name="selected_genre" value="Rock">
<input type="submit" name="selected_genre" value="I'm Feeling Lucky">
</form>
</body>
</html>
"""
#app.route("/query", methods=['GET', 'POST'])
def link():
allowed_values = ('Jazz', 'Classic', 'Rock')
genre = request.form.get("selected_genre")
if genre not in allowed_values:
genre = random.choice(allowed_values)
genre = genre.lower()
url = f"https://www.google.com/search?q={genre}"
return redirect(url)
if __name__ == "__main__":
app.run(debug=True, port=600)
If you want to use different text on button but still send the same value then you may need hidden <input> with value but then every button will need seprated <form>
#app.route("/")
def home():
return """
<html>
<body>
<h2> Spin yo records </h2>
<form action="/query" method="POST">
<input type="hidden" value="Jazz" name="selected_genre">
<input type="submit" value="The Best Jazz Music">
</form>
<form action="/query" method="POST">
<input type="hidden" value="Classic" name="selected_genre">
<input type="submit" value="The Best Classic Music">
</form>
<form action="/query" method="POST">
<input type="hidden" value="Rock" name="selected_genre">
<input type="submit" value="The Best Rock Music">
</form>
<form action="/query" method="POST">
<input type="hidden" value="random" name="selected_genre">
<input type="submit" value="I'm Feeling Lucky">
</form>
</body>
</html>
"""
Or you have to use <button> instead of <input>
#app.route("/")
def home():
return """
<html>
<body>
<h2> Spin yo records </h2>
<form action="/query" method="POST">
<button type="submit" name="selected_genre" value="Jazz">The Best Jazz Music</button>
<button type="submit" name="selected_genre" value="Classic">The Best Classic Music</button>
<button type="submit" name="selected_genre" value="Rock">The Best Rock Music</button>
<button type="submit" name="selected_genre" value="random">I'm Feeling Lucky</button>
</form>
</body>
</html>
"""

Getting None from request.form.get()

I need to get data from 2 different forms. The first "index.html" rendering takes a user's selected option and stores it on the variable "item".
on "page2.html", the "item" value is shown here just fine. And again, the user is entering some new data on the textbox input and then it's redirected to the next rendering, "page3.html"
on "page3.html", it is supposed to show both data from the forms on "index.html" and "page2.html" but i'm getting "None" when I try to show the "item" value. Why?
I've tried to do it on separate app.route() routes, but I got the None type value as well.
the app.py file:
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route("/", methods=['GET', 'POST'])
def index():
if request.method == 'POST':
if request.form.get('send') == 'send':
item = str(request.form.get('test1'))
return render_template('page2.html', item=item)
if request.form.get('send2') == 'send2':
item = str(request.form.get('test1'))
text = request.form.get('textbox')
return render_template('page3.html', item=item, text=text)
else:
return render_template('index.html')
app.run(debug=True)
the index.html file:
<div align="center">
<form method="POST" action="{{ url_for('index') }}">
<select name="test1">
<option name="1" value="1">1</option>
<option name="2" value="2">2</option>
<option name="3" value="3">3</option>
</select>
<button type="submit" name="send" value="send">Send</button>
</form>
</div>
the page2.html file:
<div align="center">
<form method="POST" action="{{ url_for('index') }}">
<h2>Previous selection: {{ item }}</h2><br>
<input type="text" name="textbox" value="Enter text">
<button type="submit" name="send2" value="send2">Send</button>
</form>
</div>
and the page3.html file:
<div align="center">
<h2>Your result is:</h2><br>
<h2>"{{ text }}" and "{{ item }}" </h2>
</div>
store the value of item in a session so that you can access it later. Once you are done you can clear the session like session.pop("session_name", None)
if request.form.get('send') == 'send':
item = str(request.form.get('test1'))
session["test1"]=item
return render_template('page2.html', item=item)
if request.form.get('send2') == 'send2':
item = session.get("test1")
text = request.form.get('textbox')
return render_template('page3.html', item=item, text=text)

Flask - pass form input to url_for

I have an HTML template that lets the user select a date via jQuery datepicker.
How can I pass the date selected into an action?
The idea is, the user selects a date, then that passes to Flask's route.py, via app.route("/date/<date>")
calendar.html
{% block topscripts %}
<link rel="stylesheet" type="text/css" href= "{{ url_for('static',filename='styles/calendar.css') }}">
<script>
$(function() {
$("#datepicker").datepicker({dateFormat: 'yy-mm-dd'});
});
</script>
{% endblock %}
{% block content %}
<form method="post" action="{{ url_for('specific_date', date='2019-04-11') }}">
<p>Date: <input type="text" id="datepicker" name='go-to-date'></p>
<input type="hidden" name="calendar-form">
<input type="submit">
</form>
{% endblock %}
So, when the user selects a date in the datepicker ID, I want to pass that date to the url_for. Currently I hardcoded the date (2019-04-11) just to check that it works, and it does. How can I have that part be dynamic to whatever the user selects in the Calendar?
...If it helps, here's in routes.py (default_template() is the function that renders the template in the end).:
#app.route("/date/<date>/", methods=["GET", "POST"])
def specific_date(date):
print("\n\nDate:", date, "\n\n")
images = get_files_on(date)
print("\n\nSpecific date images:", images)
return default_template(date=date, image_list=images)
Make a POST request to the /date route like so.
Changes to calendar.html:
{% block content %}
<form method="post" action="{{ url_for('specific_date') }}">
<p>Date: <input type="text" id="datepicker" name='go-to-date'></p>
<input type="hidden" name="calendar-form">
<input type="submit">
</form>
{% endblock %}
Changes to the date route:
from flask import request
# only allow POST request method
#app.route("/date/", methods=["POST"])
def specific_date():
# getting the date from the POST request
date = request.form['go-to-date']
print("\n\nDate:", date, "\n\n")
images = get_files_on(date)
print("\n\nSpecific date images:", images)
return default_template(date=date, image_list=images)

TypeError: Object of type 'Response' is not JSON serializable

The function extract_data() converts uploaded PDF file to JSON data.
Function form_input() is meant to post the JSON data from "extract_data" as default input form values.
Extract_data() generates JSON data but is not redirecting it to form_input()
Below are the functions -
from flask import render_template, redirect, url_for, jsonify
from werkzeug.utils import secure_filename
#app.route("/", methods=["GET", "POST"])
def extract_data(input_module=pdftotext):
if request.method == "POST":
file = request.files["file"]
if file and allowed_file(file.filename):
invoicefile = secure_filename(file.filename)
extracted_str = input_module.to_text(invoicefile)
json_data=jsonify(ext)
session['json_data'] = json_data
return redirect(url_for('form_input', json_data=json_data)
return render_template("extract.html")
Here is extract.html
<html>
<form method='POST' enctype=multipart/form-data>
<input type="file" name="file" class="btn btn-success"></input>
<br>
<button type="submit" value=Upload class="btn btn-warning">Go!
</button>
</form>
</html>
The function form_input() is meant to get the json_data from extract_data() and use it as default form input values.
#app.route("/form_input")
def form_input():
json_data = request.args['json_data']
json_data = session['json_data']
if request.method == 'POST':
submited_item = request.POST['%s' % (value1)]
return render_template("inputdata.html", json_data="json_data")
Here is input_data.html:
<html>
<form method="POST">{{ csrf_token }}
<label>Input data</label>
<input type="text">
{% for item in json_data %}
<input type="checkbox" name="{{ item.value1 }}" value="{{ item.value1 }}"> {{ item.value1 }}<br>
{% endfor %}
<button type="submit" >Submit</button>
</form>
</html>

Jinja2 html buttons: Catch POST on different pages

I've got a GAE-app that uses Jinja2 templates to serve its html pages.
Now in my main python file I've got one class, mainhandler, with a GET and a POST method. This all works for the welcome screen where there is a button to do something. When the button is clicked, the POST method is invoked which calls a second page.
I can't find anything about how to catch the button events on the second page, result.html. And make it progress methods in the main python file.
So: "How can I work with errorMail and toCalendar buttons on result.html?
This is my main file:
# -*- coding: utf8 -*-
import webapp2
from apiclient.discovery import build
from oauth2client.appengine import OAuth2Decorator
from format import formatFile
import jinja2
import os
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
decorator = OAuth2Decorator(secret)
class MainHandler(webapp2.RequestHandler):
#decorator.oauth_required
def get(self):
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render())
#processes the file and shows the results
def post(self):
# Get the authorized Http object created by the decorator.
http = decorator.http()
service = build('calendar', 'v3', http=http,
developerKey='secret')
# Make a list of calendars
calendar_list = service.calendarList().list().execute()
totalList = formatFile(self.request.get('file'))
template_values = {"totalList": totalList, "calendar_list": calendar_list}
template = jinja_environment.get_template('result.html')
self.response.out.write(template.render(template_values))
app = webapp2.WSGIApplication([('/', MainHandler)],
debug=True)
This is page index.html:
<!DOCTYPE html>
<html>
<head><title></title></head>
<body>
<form method="post">
<div><label>Select file:</label</div>
<input type="file" name="file">
<br>
<input type="submit" name="upload" value="Upload">
</form>
</body>
</html>
This is page result.html:
<html>
<head>
</head>
<body>
<h3>De volgende data staat klaar voor je agenda:</h3>
<table border="1" cellpadding="3">
<tr>
<th>Dag</th>
<th>Datum</th>
<th>Tijd</th>
<th>Omschrijving</th>
</tr>
{% for line in totalList %}
<tr>
{% for item in line %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<br>
<b>Selecteer de agende waar de diensten in geplaatst worden:</b>
<br>
<select>
{% for calendar_list_entry in calendar_list['items'] %}
<option value=>{{ calendar_list_entry['summary'] }}</option>
{% endfor %}
</select>
<br>
<form method="post">
<input type="submit" name="toCalendar" value="In kalender plaatsen">
</form>
<br>
<b>Uitvoer incorrect? Klik dan op onderstaande knop om foutmeldings-email te sturen.</b>
<form method="post">
<input type="submit" name="errorMail" value="Uitvoer incorrect!">
</form>
</body>
</html>
You do not have to receive buttons events. You receive the form data (including the buttons) in the post like the self.request.get('file')
You can add more than one button to a post.
Every form can have its own post handler, by adding an action:
index.html (results in post to /result1):
<form action="/result1" method="post">
result.html (results in post to /result2):
<form action="/result2" method="post">
<input id="toCalender " type="submit" name="toCalendar" value="In kalender plaatsen">
<br>
<b>Uitvoer incorrect? Klik dan op onderstaande knop om foutmeldings-email te sturen.</b>
<input id="errorMail" type="submit" name="errorMail" value="Uitvoer incorrect!">
</form>

Categories