How do I pass id from HTML to Python Flask [duplicate] - python

This question already has answers here:
Sending data from HTML form to a Python script in Flask
(2 answers)
Closed 1 year ago.
So I got a delete route to delete data from SQL. How do I get the id from HTML so I can Delete the data from application.py using db.execute("DELETE FROM birthdays WHERE name=?",id)
Delete method reference:https://www.digitalocean.com/community/tutorials/how-to-modify-items-in-a-one-to-many-database-relationships-with-flask-and-sqlite
This is my code:
application.py:
import os
from cs50 import SQL
from flask import Flask, flash, jsonify, redirect, render_template, request, session
# Configure application
app = Flask(__name__)
# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True
# Configure CS50 Library to use SQLite database
db = SQL("sqlite:///birthdays.db")
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
# TODO: Add the user's entry into the database
name = request.form.get("name")
month = request.form.get("month")
date = request.form.get("date")
if not name or month or date :
return render_template("error.html", message="Missing something")
db.execute("INSERT INTO birthdays (name,month,day) VALUES(?,?,?)",name,month,date)
return redirect("/")
else:
# TODO: Display the entries in the database on index.html
entries=db.execute("SELECT * FROM birthdays" )
return render_template("index.html",entries=entries)
#FIX HERE
#app.route("/delete", methods=["GET", "POST"])
def delete(id):
if request.method == "post":
db.execute("DELETE FROM birthdays WHERE name=?",id)
return redirect("/")
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght#500&display=swap" rel="stylesheet">
<link href="/static/styles.css" rel="stylesheet">
<title>Birthdays</title>
</head>
<body>
<div class="jumbotron">
<h1>Birthdays</h1>
</div>
<div class="container">
<div class="section">
<h2>Add a Birthday</h2>
<!-- TODO: Create a form for users to submit a name, a month, and a day -->
<form action="/" method="post">
<input autocomplete="off" type="text" name="name" placeholder="Name">
<input autocomplete="off" type="number"min=1 max=12 name="month" placeholder="month">
<input autocomplete="off" type="number"min=1 max=31 name="date" placeholder="date">
<input type="submit" value="submit">
</form>
</div>
<div class="section">
<h2>All Birthdays</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Birthday</th>
</tr>
</thead>
<tbody>
<!-- TODO: Loop through the database entries to display them in this table -->
{% for person in entries%}
<tr>
<td>{{person.name}}</td>
<td>{{person.day}}/{{person.month}}</td>
<!--DELETE BUTTON-->
<td><form action="{{ url_for('delete', id=person.name) }}" method='post'><input type="submit" value="delete"></form></td>
</tr>
{% endfor%}
</tbody>
</table>
</div>
</div>
</body>
</html>

For the delete route, you need to add the id variable:
#FIX HERE
#app.route('/<int:id>/delete/', methods=('POST',))
def delete(id):
https://flask.palletsprojects.com/en/1.1.x/quickstart/#variable-rules

Related

URL parameter in flask app is showing up 1 form submission late

I have a flask app that I am trying to append a url parameter to after a form submission. For example, I have a date input as a form, and if I selected 2022-02-17 and pressed the button to submit, I would want the url to then append ?date=2022-02-17. My problem is the its always 1 behind. So on first submit, it is only ?date=, and then if I chose another date that isn't 2022-02-17, the url would then update to ?date=2022-02-17. I have a print to make sure the date is being correctly passed into the handler function for that page as well.
Here is a jinja snippet of the form:
<div class="container">
<p>{{ search_date }}</p>
<form action={{ url_for('past_releases', date=search_date) }} method="POST">
<div class="input-group justify-content-center">
<div class="form-outline">
<input class="form-control" type="date" id="searchdate" name="searchdate">
</div>
<button type="submit" class="btn btn-primary">
<i class="fa fa-search"></i>
</button>
</div>
</form>
</div>
and here is the python function for handling that page:
#app.route("/past-releases", methods=["GET", "POST"])
def past_releases():
"""
Search for past releases' info, based on date.
"""
search_date = None
if request.method == "GET" and request.args.get("date"):
search_date = request.args.get("date")
elif request.method == "POST":
search_date = request.form["searchdate"]
# # we use a try here for when someone might click search on the placeholder
# # date of "mm/dd/yyyy"
print(search_date)
if search_date:
try:
date_selcted = dateutil.parser.parse(search_date).strftime("%B %d, %Y")
except ParserError:
return render_template("past_releases.jinja")
pipelines_and_tasks = get_pipeline_runs_by_date(search_date)
return render_template(
"past_releases.jinja",
date_selected=date_selcted,
pipelines_and_tasks=pipelines_and_tasks,
search_date=search_date,
)
return render_template("past_releases.jinja")
I think you should replace the "method" parameter of the form with the GET method. The parameter in url_for is then no longer required. The selected date is automatically added as a URL parameter when the form is submitted.
In the query using request.args, it is also possible to add a standard parameter as the second argument and, specifying the parameter name type, a function to convert the parameter to a date.
The example below illustrates the described possibility.
Flask (app.py)
from datetime import date
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/search')
def search():
search_date = request.args.get(
'search-date',
date.today(),
type=date.fromisoformat
)
# Your filter function here.
return render_template('search.html', **locals())
HTML (templates/search.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Search</title>
</head>
<body>
<form method="get">
<input type="date" name="search-date" value="{{search_date.isoformat()}}" />
<input type="submit" />
</form>
</body>
</html>

making a todo list using pickle and flask

i'm trying to make a simple todo list using flask and a text file database, for now I'm trying to add items and save it in a text file, and also show the items online, but I'm not sure what I'm going wrong,this is the code i have for now
from flask import Flask, render_template, request, redirect, url_for
import todo_list
app = Flask(__name__)
#app.route('/')
def index():
with open('programming.pkl', 'rb') as output:
new_list = todo_list.pickle.load(output)
return render_template('index.html', todo1=new_list, todo=todo )
#app.route('/task', methods=['GET''POST'])
def task():
if request.method == 'POST':
todo = request.form.get('todo')
with open('programming.pkl', 'wb+') as output:
todo_list.pickle.dump(todo, output)
return redirect('/')
and this is my HTML code, i have just used google.com as a place holder for now
<!DOCTYPE html>
<html>
<head>
<h1> TODO APP</h1>
</head>
<body>
<p>
<br>
<h2>Here are your todo items</h2>
</p>
<br>
<h3>{{ todo }}</h3>
<h4>Mark as done</h4>
<br>
<h3>{{ todo }}</h3>
<h4>Mark as done</h4>
<br>
<h3>stuff1</h3>
<h4>Mark as done</h4>
<br>
<form action="/task" method="POST">
<h3>Create todo</h3>
<input type="text" id="todo" name="todo"><br><br>
</form>
</body>
</html>

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

Flask-Sqlite posting to DB from webform

I have started just learning Flask and tried to find an answer how to post to SQLite DB from webform. So far haven't managed to get it work and bit lost with this. I manage to print values from DB based on code sample from simplypython but don't know how to add new ones from webform.
I would need to be able to address elements, open connection to the database, Insert values, save and close the connection. As far as I am aware I should add the POST method to the app.py and use request.form statement to pull the elements when submit button is pressed.
then the code should automatically display all the values on index html, which already works. could you please help me with the code I need to add to app.py file to get values added to DB and what to add to form action webform-section on html file?
index.html
<!DOCTYPE html>
<html>
<head>
<title>Flask Intro</title>
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
</head>
<body>
<div class="container">
<h3 potsit </h3>
{% for post in posts %}
Titleotsikko: {{post.title }} <br>
Postotsikko: {{post.description}}
{% endfor %}
</div>
<div>
<form action="/????????????NOT SURE WHERE TO DIRECT" method="post">
<div>
<label for="title">title:</label>
<input type="text" id="title" />
</div>
<div>
<label for="description">description:</label>
<input type="text" id="description" />
</div>
<div class="button">
<button type="submit">Add to db</button>
</div>
</form>
</div>
</body>
</html>
app.py
from flask import Flask, render_template, request, session, g
import sqlite3
app = Flask(__name__)
#app.route('/')
def home():
g.db = sqlite3.connect("sample.db")
cur = g.db.execute('select * from posts')
posts = [dict(title=row[0], description=row[1]) for row in cur.fetchall()]
g.db.close()
return render_template("index.html", posts=posts)
if __name__=='__main__':
app.run(debug=True)
sql.py
import sqlite3
with sqlite3.connect("sample.db") as connection:
c = connection.cursor()
c.execute("DROP TABLE posts")
c.execute("CREATE TABLE posts(title TEXT, description TEXT)")
c.execute('INSERT INTO posts VALUES("Luck", "no luck.")')
EDIT
I made some changes based on the suggestion from Paul Rooney and created file called post.html, moved the form there from index.htmland added another #route on the app.py file. I believe I'm not far off but after trying to figure this out since Tuesday hoping to get it work. Unfortunately now I receive an error 405 'The method is not allowed for the requested URL.' and I'm stuck with this after trying different options.
post.html
<!DOCTYPE html>
<html>
<head>
<title>Flask post</title>
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
</head>
<body
<div>
<form action="/post" method="post">
<div>
<label for="title">title:</label>
<input type="text" id="title" />
</div>
<div>
<label for="description">description:</label>
<input type="text" id="description" />
</div>
<div class="button">
<button type="submit">Add to db</button>
</div>
</form>
</div>
</body>
</html>
(edited) app.py
from flask import Flask, render_template, request, session, g, redirect, url_for
import sqlite3
app = Flask(__name__)
#app.route('/')
def home():
g.db = sqlite3.connect("sample.db")
cur = g.db.execute('select * from posts')
posts = [dict(title=row[0], description=row[1]) for row in cur.fetchall()]
g.db.close()
return render_template("index.html", posts=posts)
#app.route('/post', methods=['POST'])
def post():
title=request.form['title']
description=request.form['description']
print title, description
return redirect(url_for('/'))
if __name__=='__main__':
app.run(debug=True)
To answer your original question you should pick an endpoint to post your data to and use that in both your flask app and the html form action.
I suggested post in comments but it could be anything. I think you've grasped what to do here but for completeness.
The HTML
<form action="/post" method="post">
The Python Code
Add a route to handle the POSTed data.
#app.route('/post', methods=['POST'])
def post():
# Do db stuff then redirect back to index page.
pass
Edited Question
You have an indentation error in your code, which is causing the 405 error. Your post function is inside your home function. Make it more like this
app.py
from flask import Flask, render_template, request, session, g, redirect, url_for
import sqlite3
app = Flask(__name__)
#app.route('/')
def home():
g.db = sqlite3.connect("sample.db")
cur = g.db.execute('select * from posts')
posts = [dict(title=row[0], description=row[1]) for row in cur.fetchall()]
g.db.close()
return render_template("index.html", posts=posts)
# One level of indentation removed from here downwards
#app.route('/post', methods=['POST'])
def post():
title=request.form['title']
description=request.form['description']
return redirect(url_for('home'))
if __name__=='__main__':
app.run(debug=True)
After that I see a 400 Bad Request error, which stems from the fact that you don't have name parameters in your html form.
The attempt to access the title and description values in the form dict in app.py will throw a KeyError exception, as those keys will not be present without the name parameter in the html.
If you add them e.g.
...
<input type="text" id="title" name='title'/>
...
<input type="text" id="description" name='description'/>
...
Then it will run all the way through your function.
The next issue will be
redirect(url_for('/'))
instead use
redirect(url_for('home'))
home being the name of the function called for path '/'.
After that you should be good to go.

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