I have a small Flask app that processes form input and displays the results on the same page using HTMX. When the page loads, the default form values are used to calculate the results. This is done with hx-trigger="load" on the form. But if new values are input to the form and submitted, then the results do not update. If I remove the hx-trigger="load" from the form, everything works fine but the form does not get submitted when the page first loads. How can I use HTMX to submit the form when the page loads as well as submit the form when the "Submit" button is clicked?
The Flask app.py is given below.
from flask import Flask
from flask import render_template
from flask import request
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/results', methods=['POST'])
def results():
values = request.form['values']
multiplier = request.form['multiplier']
vals = list(map(int, values.split(', ')))
mult = int(multiplier)
y = []
for val in vals:
y.append(val * mult)
return render_template('results.html', results=y)
The index.html template is shown below.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<title>Home Page</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col">
<h1 class="mt-3">Submit values</h1>
<p>Input using comma separated values then click the submit button to see results.</p>
<form hx-post="/results" hx-target="#results" hx-trigger="load">
<div class="mb-3">
<label for="values" class="form-label">Values</label>
<input type="text" class="form-control" style="max-width:200px;" name="values" value="1, 2, 3, 4, 5">
</div>
<div class="mb-3">
<label for="multiplier" class="form-label">Multiplier</label>
<input type="text" class="form-control" style="max-width:200px;" name="multiplier" value="3">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
<div class="col">
<div id="results"></div>
</div>
</div>
</div>
<script src="https://unpkg.com/htmx.org#1.6.1" integrity="sha384-tvG/2mnCFmGQzYC1Oh3qxQ7CkQ9kMzYjWZSNtrRZygHPDDqottzEJsqS4oUVodhW" crossorigin="anonymous"></script>
</body>
</html>
The results.html template is shown below.
<h1 class="mt-3">Results</h1>
<p>Below are the results using the form inputs.</p>
{{ results }}
When you provide a trigger event for HTMX, the default one is replaced with it. For a form element, the default event is the submit. Fortunately HTMX support multiple triggers, we just have to separate them by commas. So, just add submit to the hx-trigger attribute and HTMX will listen to submit events again:
<form hx-post="/results" hx-target="#results" hx-trigger="submit, load">
Related
Snippet of HTML:
<div class="split left" >
<div class="centered">
<div class="container">
<p>Hi!</p>
</div>
<div class="darker">
<p>{{message}}</p>
</div>
<form action="{{ url_for('index')}}" method="post">
<input type="text" name="client" placeholder="Enter Message" class="text" id="message">
<button class="glow-on-hover">Send</button>
</form>
Snippet of FLASK code:
#app.route("/")
def start():
return render_template("index.html")
#app.route("/", methods=["POST"])
def index():
message = request.form.get("client")
return render_template("index.html", message=message)
Whenever I enter value and press send button it overwrites but I want to print a new paragraph each time I press send button. The list can not be useful in my opinion because it is something like a chat app. So the list will only display one recipient's message. Any effective and easy way???
here I have recreated your code and it seems working fine. Usually, the form refreshes the current page which leads to overwriting of contents.
Python Code:
#app.route("/")
def start():
return render_template("index.html")
#app.route("/message")
def index():
message = request.args.get("msg")
return message
HTML Code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
function textGen(){
$.get('/message', {"msg": document.getElementById('message').value }).done(function(data){
document.getElementById("text").innerHTML += `<p>${data}</p>`;
})
}
</script>
</head>
<body>
<div class="split left">
<div class="centered">
<div class="container">
<p>Hi!</p>
</div>
<div class="darker" id="text">
</div>
<input type="text" name="client" placeholder="Enter Message" class="text" id="message">
<button class="glow-on-hover" onclick="textGen()">Send</button>
</body>
</html>
In both the components, I have created msg handler in flask and msg sender in javascript using jquery. So this solution will work in your scenario. Take a look and please write to me if you face any errors or difficulties in making this code work. And I'm not using the form so it will prevent overwriting.
I am new to building API. I am building a very Simple API: When executed, The HTML page displaying API will ask to enter name: the Api will just return: "Hello {name}". I am getting 405 Error. Here is my code for App.py and HTML page:
from app import app
from flask import render_template
from flask import request
#app.route('/')
def home():
return "hello no world!"
#app.route('/template',methods=['POST','GET'])
def template():
output = [request.form.values()]
output = output[0]
return render_template('home.html',prediction_text='Hello {}'.format(output))
and my HTML code for home.html:
!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Welcome home</title>
</head>
<body>
<div class="login">
<h1>Enter your name</h1>
<!-- Main Input For Receiving Query-->
<form action="{{ url_for('template')}}"method="post">
<input type="text" name="name" placeholder="Name" required="required" />
<button type="submit" class="btn btn-primary btn-block btn-large">Enter</button>
</form>
<br>
<br>
{{ prediction_text }}
</div>
</body>
</html>
I have looked at several other StackOverflow forums with just this issue. It seems like there is something wrong with "GET" or "POST" method but I can not seem to figure out what? Maybe one of you could see something I did not. I am running this API Inside a Docker so "app = Flask(name)" are stored elsewhere , If that is relevant.
Method Not Allowed - The method is not allowed for the requested URL
Flask - POST - The method is not allowed for the requested URL
https://www.reddit.com/r/flask/comments/a04aew/ask_flask_help_me_to_find_the_error_in_my_code/
This question is now resolved. I changed two things:
From HTML:
Changing:
<form action="{{ url_for('template')}}"method="post">
to
<form action="{{ url_for('template')}}"method="get">
And Changing from Flask API:
output = [request.form.values()]
output = output[0]
return render_template('home.html',prediction_text='Hello {}'.format(output))
to
output = request.args.values()
return render_template('home.html',prediction_text="Hello {}?".format(list(output)[:]))
The application is supposed to take in a user email address with the user height. Then it should send an email to the email address and send them a verification email, along with an Average height which is computed by finding the average of all inputted email addresses.
There is a problem, whenever i press on confirm button it redirects me to an error page: 404 PAGE NOT FOUND.
app.py code:
from flask import Flask, render_template, request
from flask.ext.sqlalchemy import SQLAlchemy
from send_email import send_email
from sqlalchemy.sql import func
app=Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI']='postgresql://postgres:postgres123#localhost/height_collector'
db=SQLAlchemy(app)
class Data(db.Model):
__tablename__="data"
id=db.Column(db.Integer, primary_key=True)
email_=db.Column(db.String(120), unique=True)
height_=db.Column(db.Integer)
def __init__(self, email_, height_):
self.email_=email_
self.height_=height_
#app.route("/")
def index():
return render_template("index.html")
#app.route("/success", methods=['POST'])
def success():
if request.method=='POST':
email=request.form["email_name"]
height=request.form["height_name"]
print(email, height)
if db.session.query(Data).filter(Data.email_ == email).count()== 0:
data=Data(email,height)
db.session.add(data)
db.session.commit()
average_height=db.session.query(func.avg(Data.height_)).scalar()
average_height=round(average_height, 1)
count = db.session.query(Data.height_).count()
send_email(email, height, average_height, count)
print(average_height)
return render_template("success.html")
return render_template('index.html', text="Seems like we got something from that email once!")
if __name__ == '__main__':
app.debug=True
app.run(port=5005)
send_email.py code:
from email.mime.text import MIMEText
import smtplib
def send_email(email, height, average_height, count):
from_email="myemail#gmail.com"
from_password="mypassword"
to_email=email
subject="Height data"
message="Hey there, your height is <strong>%s</strong>. <br> Average height of all is <strong>%s</strong> and that is calculated out of <strong>%s</strong> people. <br> Thanks!" % (height, average_height, count)
msg=MIMEText(message, 'html')
msg['Subject']=subject
msg['To']=to_email
msg['From']=from_email
gmail=smtplib.SMTP('smtp.gmail.com',587)
gmail.ehlo()
gmail.starttls()
gmail.login(from_email, from_password)
gmail.send_message(msg)
index.html code:
<!DOCTYPE html>
<html lang="en">
<title> Data Collector App</title>
<head>
<link href="../static/main.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>Collecting height</h1>
<h3>Please fill the entires to get population statistics on height</h3>
<div class="email"> {{text | safe}} </div>
<form action="{{url_for('success')}}" method="POST">
<input title="Your email will be safe with us" placeholder="Enter your email address" type="email" name="email_name" required> <br>
<input title="Your data will be safe with us" placeholder="Enter your height in cm" type="number" min="50", max="300" name="height_name" required> <br>
<button type="submit"> Submit </button>
</form>
</div>
</body>
</html>
success.html code:
<!DOCTYPE html>
<html lang="en">
<title> Data Collector App</title>
<head>
<link href="../static/main.css" rel="stylesheet">
</head>
<body>
<div class="container">
<p class="success-message"> Thank you for your submission! <br>
You will receive an email with the survey results shortly.
</p>
</div>
</body>
</html>
I have a Python code.
import cx_Oracle
import re
from flask import Flask, render_template, request, url_for, redirect
app = Flask(__name__)
app.config['SECRET_KEY'] = 'd369342136ecd032f8b4a930b6bb2e0e'
#app.route('/add')
def edited():
connect = cx_Oracle.connect("********", "********", "******/XE")
cursor = connect.cursor()
cod_ed = request.form['cod_ed']
nome_ed = request.form['nome_ed']
endereco = request.form['endereco']
telefone = request.form['telefone']
cidade = request.form['cidade']
execute = """INSERT INTO editor VALUES
(:cod_ed, :nome_ed, :endereco, :telefone, :cidade)"""
cursor.execute(execute, {'cod_ed':cod_ed, 'nome_ed':nome_ed, 'endereco':endereco, 'telefone':telefone, 'cidade':cidade})
connect.commit()
#app.route('/', methods=['GET', 'POST'])
def add_data():
return render_template('forms.html')
#app.route('/post_data', methods=['GET','POST'])
def post_data():
return redirect(url_for('edited'))
if __name__ == "__main__":
app.run(host = 'localhost', port = 8000, debug=True)
And its html correspondante:
<!DOCTYPE html>
<html>
<head>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous">
</script>
<title>Base de dados</title>
</head>
<body>
<form methods="POST" action="/post_data">
<div class="col-lg-2">
<div class="form-group">
<label for="element-1" >Codigo:</label>
<input id="cod_ed" name="cod_ed" type="text" class="form-control" placeholder="Codigo"/>
<label for="element-2" >Nome:</label>
<input id="nome_ed" name="nome_ed" type="text" class="form-control" placeholder="Nome"/>
<label for="element-3" >Endereço:</label>
<input id="endereco" name="endereco" type="text" class="form-control" placeholder="Endereço"/>
<label for="element-4" >Telefone:</label>
<input id="telefone" name="telefone" type="text" class="form-control" placeholder="Telefone"/>
<label for="element-5" >Cidade:</label>
<input id="cidade" name="cidade" type="text" class="form control" placeholder="Cidade"/>
<input class="btn btn-info" type="submit" value="Enter">
</div>
</div>
</div>
</form>
</body>
</html>
I'm relatively new to Flask and Python in general. When I run the forms, they get displayed, but when I try to insert them into the database, I get this:
werkzeug.exceptions.HTTPException.wrap.<locals>.newcls: 400 Bad Request: KeyError: 'cod_ed'
What exactly is causing it and how do I fix it?
When posting to /post_data endpoint the browser recieves a redirection code, and then gets /add without posting any data, provoking a key error.
You are fetching form elements at the wrong place. You should do the database insertion logic inside /post_data and redirect to /add afterwards.
Here you are posting the form data to /post_data and redirect it to /add so the edited function will not be able to access the request object containing the form data. So just change the form action to /add to make it work correctly.
I'm new to Python & Flask and trying to set up a very basic script that takes information submitted from a form and posts it to a new page (I know, very simple right?)
I'm having limited success and can't figure out what the problem is here. It's working when I have 2 out of the 4 form fields selected in the python file:
name=request.form['name']
age=request.form['age']
This works fine and does what I expect it to do - Renders the output.html page containing 'name' & 'age'
but as soon as I try to add any more, I'm getting an internal server error (500), even through I'm copying & pasting the exact same code and only changing the variables (i.e 'number' & 'feeling') - In both the .py file and the input & output html files.
Heres the code..
Python code:
(The input form is on the /input/ page. "input_1" renders the output.html file)
from flask import Flask, render_template, request, url_for, redirect
from dbconnect import connection
from flask_debugtoolbar import DebugToolbarExtension
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'kuywergiukrewgkbyuwe'
toolbar = DebugToolbarExtension(app)
app.config.update(TEMPLATES_AUTO_RELOAD = True)
#app.route('/')
def homepage():
return render_template ("main.html")
#app.route('/input/')
def input():
return render_template ("input.html")
#app.route('/input/', methods=["POST"])
def input_1():
name=request.form['name']
age=request.form['age']
number=request.form['number']
feeling=request.form['feeling']
return render_template('output.html', name = name, age = age, number = number, feeling = feeling)
if __name__ == "__main__":
app.run()
The input.html file:
(Contains the input form)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>devserver</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="{{ url_for("static", filename="css/bootstrap.css") }}">
<link rel="shortcut icon" href="{{ url_for("static", filename="favicon.ico") }}">
</head>
<body>
<div class="container">
<div class="col">
<h2>Input form</h2>
<br>
<div class="form-group" >
<form method="post" action="{{ url_for('input') }}">
<label for="InputForm">Name</label>
<input type="text" name="name" class="form-control"/>
<label for="InputForm">Age</label>
<input type="text" name="age" class="form-control"/>
<label for="InputForm">Number</label>
<input type="text" name="number" class="form-control"/>
<label for="InputForm">Feeling</label>
<input type="text" name="feeling" class="form-control"/>
<br>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</body>
</html>
The output.html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>devserver</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="{{ url_for("static", filename="css/bootstrap.css") }}">
<link rel="shortcut icon" href="{{ url_for("static", filename="favicon.ico") }}">
</head>
<body>
<div class="container">
<div class="col">
<h2>Output form</h2>
<br>
<div class="form-group" >
<form>
<h3>Output 1</h3>
<P>Your name is = {{name}}</p>
<h3>Output 2</h3>
<P>Your age is = {{age}} </p>
<h3>Output 3</h3>
<P>Your number is = {{number}}</p>
<h3>Output 4</h3>
<P>Your feeling is = {{feeling}} </p>
</form>
</div>
</div>
</div>
</body>
</html>
I cant understand why it works with only 2. When I comment out the following it works fine:
#app.route('/input/', methods=["GET","POST"])
def input():
name=request.form['name']
age=request.form['age']
#number=request.form['number']
#feeling=request.form['feeling']
return render_template('output.html', name = name, age = age) #number = number, feeling = feeling)
It's probably something quite obvious but I just can't see it.
Thanks for the help!
You usually use url_for when you have to generate urls. I would rather not complicate matter when I have to pass multiple parameters.What I would do is just this :
<form method="post" action="/input">
and in the app file :
#app.route('/input', methods=["POST"])
def input_1():
name=request.form['name']
age=request.form['age']
number=request.form['number']
feeling=request.form['feeling']
return render_template('output.html', name = name, age = age, number = number, feeling = feeling)
But if you really wanna generate urls then put the function you want to generate url for and also pass the arguments .
<form method="post" action={{url_for('input_1',name=name)}}>
and then call funtion input_1 like this:
#app.route('/input/<name>') #you can add parameters as per your wish
def input_1(name):
...
...