<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded',()=>{
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect',()=>{
console.log('connected');
document.querySelector('#submit').onclick =() =>{
const user= document.querySelector('#user').value;
const room = document.querySelector('#room').value;
socket.emit('join',{'user':user,'room':room});
console.log('emitted');
return false;
};
});
});
</script>
</head>
<body>
<form id="new-task" action="{{ url_for('chat') }}" method="post">
<input type="text" autocomplete="off" autofocus id="user" placeholder="username">
<input type="text" autocomplete="off" autofocus id="room" placeholder="room">
<input type="submit" id="submit" value="join">
</form>
</body>
</html>
localhost:5000 open index.html page , when i click on submit only socketio is working but url is not being changed .
import os
import requests
from flask import Flask,jsonify,render_template,request
from flask_socketio import SocketIO,emit,join_room,leave_room,send
from werkzeug import secure_filename
app = Flask(__name__)
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")
socketio=SocketIO(app)
#app.route("/")
def index():
return render_template("index.html")
#app.route("/chat" ,methods=["POST"])
def chat():
print("running chat")
return render_template("chat.html")
#socketio.on('join')
def on_join(data):
username = data['user']
room = data['room']
join_room(room)
emit('chat',{'username':username},room=room)
print("room has been allocated")
when i did only url_for without socketio i was able to change route but now it does not seem to work. in console and cmd i am able to see socketio working but chat route is not working
Related
I am facing the following issue while trying to pass a value from an HTML form <input> element to the form's action attribute and send it to the FastAPI server.
This is how the Jinja2 (HTML) template is loaded:
# Test TEMPLATES
#app.get("/test",response_class=HTMLResponse)
async def read_item(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
My HTML form:
<form action="/disableSubCategory/{{subCatName}}">
<label for="subCatName">SubCategory:</label><br>
<input type="text" id="subCatName" name="subCatName" value=""><br>
<input type="submit" value="Disable">
</form>
My FastAPI endpoint to be called in the form action:
# Disable SubCategory
#app.get("/disableSubCategory/{subCatName}")
async def deactivateSubCategory(subCatName: str):
disableSubCategory(subCatName)
return {"message": "SubCategory [" + subCatName + "] Disabled"}
The error I get:
"GET /disableSubCategory/?subCatName=Barber HTTP/1.1" 404 Not Found
What I am trying to achieve is the following FastAPI call:
/disableSubCategory/{subCatName} ==> "/disableSubCategory/Barber"
Anyone who could help me understand what I am doing wrong?
Thanks.
Leo
Option 1
You can have the category name defined as Form parameter in the backend, and submit a POST request from the frontend using an HTML <form>, as described in Method 1 of this answer.
app.py
from fastapi import FastAPI, Form, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory='templates')
#app.post('/disable')
def disable_cat(cat_name: str = Form(...)):
return f'{cat_name} category has been disabled.'
#app.get('/', response_class=HTMLResponse)
def main(request: Request):
return templates.TemplateResponse('index.html', {'request': request})
templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Disable a category</h1>
<form method="post" action="/disable">
<label for="cat_name">Enter a category name to disable:</label><br>
<input type="text" id="cat_name" name="cat_name">
<input class="submit" type="submit" value="Submit">
</form>
</body>
</html>
Option 2
You can have the category name declared as query parameter in your endpoint, and in the frontend use a similar approach to the one demonstrated in your question to convert the value form the form <input> element into a query parameter, and then add it to the query string of the URL (in the action attribute).
Note that the below uses a GET request in contrast to the above (in this case, you need to use #app.get() in the backend and <form method="get" ... in the frontend, which is the default method anyway). Beware that most browsers cache GET requests (i.e., saved in browser's history), thus making them less secure compared to POST, as the data sent are part of the URL and visible to anyone who has access to the device. Thus, GET method should not be used when sending passwords or other sensitive information.
app.py
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory='templates')
#app.get('/disable')
def disable_cat(cat_name: str):
return f'{cat_name} category has been disabled.'
#app.get('/', response_class=HTMLResponse)
def main(request: Request):
return templates.TemplateResponse('index.html', {'request': request})
templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Disable a category</h1>
<form method="get" id="myForm" action='/disable{{ cat_name }}'>
<label for="cat_name">Enter a category name to disable:</label><br>
<input type="text" id="cat_name" name="cat_name">
<input class="submit" type="submit" value="Submit">
</form>
</body>
</html>
If you instead would like to use a POST request—which is a little safer than GET, as the parameters are not stored in the browser's history, and which makes more sense when updating content/state on the server, compared to GET that should be used when requesting (not modifying) data—you can define the FastAPI endpoint with #app.post() and replace the above template with the below (similar to Method 2 of this answer), which submits the form using POST method after transforming the form data into query parameters:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("myForm").addEventListener("submit", function (e) {
var myForm = document.getElementById('myForm');
var qs = new URLSearchParams(new FormData(myForm)).toString();
myForm.action = '/disable?' + qs;
});
});
</script>
</head>
<body>
<h1>Disable a category</h1>
<form method="post" id="myForm">
<label for="cat_name">Enter a category name to disable:</label><br>
<input type="text" id="cat_name" name="cat_name">
<input class="submit" type="submit" value="Submit">
</form>
</body>
</html>
Option 3
You can still have it defined as path parameter, and use JavaScript in the frontend to modify the action attribute of the <form>, by passing the value of the form <input> element as path parameter to the URL, similar to what has been described earlier.
app.py
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory='templates')
#app.post('/disable/{name}')
def disable_cat(name: str):
return f'{name} category has been disabled.'
#app.get('/', response_class=HTMLResponse)
def main(request: Request):
return templates.TemplateResponse('index.html', {'request': request})
templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("myForm").addEventListener("submit", function (e) {
var myForm = document.getElementById('myForm');
var catName = document.getElementById('catName').value;
myForm.action = '/disable/' + catName;
});
});
</script>
</head>
<body>
<h1>Disable a category</h1>
<form method="post" id="myForm">
<label for="catName">Enter a category name to disable:</label><br>
<input type="text" id="catName" name="catName">
<input class="submit" type="submit" value="Submit">
</form>
</body>
</html>
Option 4
If you would like to prevent the page from reloading/redirecting when hitting the submit button of the HTML <form> and rather get the results in the same page, you can use Fetch API, a JavaScript interface/library, to make an asynchronous HTTP request, similar to this answer, as well as this answer and this answer. Additionally, one can call the Event.preventDefault() function, as described in this answer, to prevent the default action. The example below is based on the previous option (i.e., Option 3); however, the same approach below (i.e., making an asynchronous HTTP request) can also be used for Options 1 & 2 demonstrated earlier, if you would like to keep the browser from refreshing the page on <form> submission.
app.py
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory='templates')
#app.post('/disable/{name}')
def disable_cat(name: str):
return f'{name} category has been disabled.'
#app.get('/', response_class=HTMLResponse)
def main(request: Request):
return templates.TemplateResponse('index.html', {'request': request})
templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("myForm").addEventListener("submit", function (e) {
e.preventDefault() // Cancel the default action
var catName = document.getElementById('catName').value;
fetch('/disable/' + catName, {
method: 'POST',
})
.then(resp => resp.text()) // or, resp.json(), etc.
.then(data => {
document.getElementById("response").innerHTML = data;
})
.catch(error => {
console.error(error);
});
});
});
</script>
</head>
<body>
<h1>Disable a category</h1>
<form id="myForm">
<label for="catName">Enter a category name to disable:</label><br>
<input type="text" id="catName" name="catName">
<input class="submit" type="submit" value="Submit">
</form>
<div id="response"></div>
</body>
</html>
Just to provide you a feedback and keep track about the solution I've put in place.
As mentioned by #Chris, I went to the proposed solution 3.
Please find below my new code:
== FastAPI ==
# Test TEMPLATES
#app.get("/test",response_class=HTMLResponse)
async def read_item(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
# Disable SubCategory
#app.post("/disableSubCategory/{subCatName}")
async def deactivateSubCategory(subCatName: str):
disableSubCategory(subCatName)
return {"message": "Sub-Category [" + subCatName + "] Disabled"}
# Enable SubCategory
#app.post("/enableSubCategory/{subCatName}")
async def activateSubCategory(subCatName: str):
enableSubCategory(subCatName)
return {"message": "Sub-Category [" + subCatName + "] Enabled"}
== HTML ==
<html>
<head>
<title>Item Details</title>
<link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet">
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("disableSubCategory").addEventListener("submit", function (e) {
var myForm = document.getElementById('disableSubCategory');
var disableSubCatName = document.getElementById('id_disableSubCategory').value;
myForm.action = '/disableSubCategory/' + disableSubCatName;
});
});
</script>
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.getElementById("enableSubCategory").addEventListener("submit", function (e) {
var myForm2 = document.getElementById('enableSubCategory');
var enableSubCatName = document.getElementById('id_enableSubCategory').value;
myForm2.action = '/enableSubCategory/' + enableSubCatName;
});
});
</script>
</head>
<body>
<form id="disableSubCategory" enctype="multipart/form-data" method="post">
<label for="subCatName">SubCategory:</label><br>
<input type="text" id="id_disableSubCategory" value=""><br>
<input type="submit" value="Disable" id="disable">
</form>
<form id="enableSubCategory" enctype="multipart/form-data" method="post">
<label for="subCatName">SubCategory:</label><br>
<input type="text" id="id_enableSubCategory" value=""><br>
<input type="submit" value="Enable" id="enable">
</form>
</body>
</html>
I know this question has been asked before but I have been through all the posts and none of the solutions seem to work for me.
Please bear with me. I am new to Flask and html and trying to build my first web app.
It's supposed to work as follows: The user uploads an Excel workbook and the workbook headers are displayed in a dropdown list using the html "select" tag. The user should then select one of the headers. I would then like to pass the selected header into a function.
I am able display the workbook headers in the dropdown list, but when I select a header, nothing happens. Does anyone have any idea what I'm doing wrong?
Please see python code below:
import flask
from flask import Flask
from flask import request
import pandas as pd
app = Flask(__name__)
#app.route("/", methods=["GET", "POST"])
def index():
global headers_list
headers_list=[]
if request.method == "POST":
df=request.files["file"]
if df:
df=pd.read_excel(df)
headers_list=get_headers(df)
selected_header = request.form.get("header")
print(str(selected_header)) #to test the code
else:
print("No file selected")
return (flask.render_template("./index.html", headers_list=headers_list))
def get_headers(dataframe):
headers=list(dataframe.columns)
return headers
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8080, debug=True)
HTML below:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="description" content=""
<title><Contra Tool</title>
</head>
<body>
<h1>Contra Tool</h1>
<p>Describe functionality</p>
<br/>
</body>
<form action="" method="post" enctype=multipart/form-data>
<label for ="myfile" > Select a file:</label>
<input type="file" id="myfile" name="file" EnableViewState=True>
<input type="submit" value="Upload">
</form>
<br><br>
<div class="dropdown">
<button class="dropbtn">Dropdown</button>
<div>
<form action="" method="POST">
<SELECT class="dropdown-content" name="header" method="POST" action="/">
<ul>
<option value="{{headers_list[0]}}" selected>{{headers_list[0]}}</option>
{% for h in headers_list[1:]%}
<option value="{{h}}">{{h}}</option>
{% endfor %}
</ul>
</SELECT>
</form>
</div>
</div>
<br/>
<input type="submit">
</html>
Since I am assuming that you do not want to save the excel file on the server, in my opinion there remains a variant in which the file is transferred twice.
If the user selects a file, it is transferred in the background to query the header columns. The select element is filled with the information received.
From now on a column can be selected and the form can be transferred.
In my example there are two routes. One to display and process the form and another which on request returns the header columns in JSON format.
from flask import Flask
from flask import abort, jsonify, render_template, request
import pandas as pd
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def upload():
if request.method == 'POST' and 'file' in request.files:
file = request.files['file']
df = pd.read_excel(file)
head = request.form.get('head');
print(f'selected "{head}"')
return render_template('index.html')
#app.route('/headers', methods=['POST'])
def headers():
if 'file' in request.files:
file = request.files['file']
df = pd.read_excel(file)
headers = list(df.columns)
return jsonify(headers=headers)
abort(400)
If the user selects a file, it is sent to the second route via AJAX. The select element is emptied and refilled and all necessary further elements are made available after the response from the server has been received.
If the user presses submit, the completed form is sent with the file and the selected column.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file" id="file" />
<select name="head" id="head" disabled>
<option>Choose Header</option>
</select>
<input type="submit" id="submit" disabled />
</form>
<script type="text/javascript">
(() => {
const fileElem = document.getElementById('file');
fileElem.addEventListener('change', evt => {
const formData = new FormData();
formData.append('file', evt.target.files[0]);
fetch('/headers', { method: 'POST', body: formData })
.then(resp => resp.json())
.then(data => {
// clear select options
const selectElem = document.getElementById('head');
for (let i=selectElem.options.length-1; i >= 0; --i) {
selectElem.remove(i);
}
// populate select options
const headers = data['headers'];
for (const head of headers) {
const optElem = document.createElement('option');
optElem.value = head;
optElem.innerHTML = head;
selectElem.append(optElem);
}
selectElem.disabled = false;
const elem = document.getElementById('submit');
elem.disabled = false;
});
});
})();
</script>
</body>
</html>
Remember, this is a minimal example and may need to be adapted and revised to meet your requirements.
I am making a flask chat application using flask-socketio. When I click the send button no messages are displayed on the screen. I have referenced the flask-socketio documentation for setting everything up. Can someone please help me here.
mainapp.py snippet
from flask_socketio import SocketIO, send, emit
app = Flask(__name__)
app.secret_key = 'replace later'
# Initialise Flask-Socketio
socketio = SocketIO(app)
#app.route("/chat", methods=['GET', 'POST'])
def chat():
return render_template('chat.html')
#socketio.on('message')
def message(data):
#print(f"\n{data}\n")
send(data)
chat.html snippet
<div id="display-message-section">
</div>
<!-- Input area -->
<div id="input-area">
<input type="text" id="user_message" placeholder="Type here..." autocomplete="off">
<button type="button" id="send_message">SEND</button>
</div>
<!-- SocketIO JS -->
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I="
crossorigin="anonymous"></script>
<!-- Custom SocketIO JS -->
<script src="{{ url_for('static', filename='scripts/socketio.js') }}"></script>
socketio.js (which is in static/scripts) snippet
document.addEventListener('DOMContentLoaded', () => {
var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('connect', () => {
socket.send("I am connected");
});
socket.on('message', data =>{
const p = document.createElement('p');
const br = document.createElement('br');
p.innerHTML = data;
document.querySelector('#display-message-section').append(p);
});
document.querySelector('#send_message').onclick = ()=>{
socket.send(document.querySelector('#user_message').value);
}
})
What the output looks like - Link to image 1
What is should look like - Link to image 2
Tried clearing the cookies and cache and everything is working fine right now.
i have created a form for user input, and run it in app.py with flask. What i want to achieve is to be able to get the user data, and then save it to my mongodb database. This is my html for the form.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../static/Recording.css">
</head>
<div class="header">
<h1>Create a recording</h1>
</div>
<body>
<div class="create">
<form name="formdata">
<label for="trolley">Trolley</label>
<input type="text" id=trolley name="trolley" size="180" required>
<label for="datetime">Date & time</label><br>
<input type="datetime-local" id="datetime" name="datetime"><br required>
<label for="temp">Temperature</label>
<input type="text" id="temp" name="temp" size="180" required>
<input id="button1" type="submit" value="Record"">
</form>
<script src="https://cdn.jsdelivr.net/npm/papaparse#5.2.0/papaparse.min.js"></script>
<script src="../static/Recording.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="../static/database.js"></script>
</body>
</html>
This is my app.py file
from flask import Flask, render_template, jsonify, json, request
from flask_pymongo import PyMongo
from pymongo import MongoClient
app = Flask(__name__)
#routes
from q2 import subroutes
client = MongoClient("mongodb+srv://Stanislas:reddevils97#tma.p1mbs.mongodb.net/<dbname>?retryWrites=true&w=majority")
db = client.get_database("smarttrolley")
records = db.recordings
#app.route("/")
def home():
return render_template('SmartTrolley.html')
#app.route("/userinput/")
def userInput():
return render_template('Recording.html')
if __name__=='__main__':
app.debug = True
app.run(port=5000)
'''
this is my subroutes.py file, which handles the user data and post it in the route "/data"
from flask import Flask
from app import app
from q2.models import User
#app.route("/data", methods=["POST"])
def data():
return User().input()
This is my models.py file, which collects the data from the "/data" route, which then proceeds to insert the data into my mongoDB database.
from flask import Flask, jsonify, request
from app import db, records
class User:
def input(self):
inputs = {
"_id": "",
"trolleyname": request.form.get('trolley'),
"Date & Time": request.form.get('datetime'),
"Temperature": request.form.get('temp')
}
db.records.insert_one(inputs)
return jsonify(inputs), 200
I am using jquery ajax call for the data to be sent. I am able to receive the data via the console.log everytime the user submits data, and everything is working fine. However, i am receiving the error "GET http://localhost:5000/data 404 (NOT FOUND)", which shows that they can't locate my "/data" route. Is anyone able to help me and find out whats wrong, because i have done the importing of the subroutes into my app.py and nothing seems to be working. thank you
It is only allowed for POST method in your code
#app.route("/data", methods=["POST"])
def data():
return User().input()
#You are using GET method here
GET http://localhost:5000/data 404 (NOT FOUND)
Either change above code to following:
#app.route("/data", methods=["GET"])
def data():
return User().input()
#Or you can hit POST request
I am creating the web app, which will process data, so I am using Server Sent Event in Flask.
I need to store session variable in a SSE function, but flask doesn't see it outside the function. Can I fix it somehow?
MWE:
server.py:
from flask import Flask, render_template, session, Response, stream_with_context
import time
app = Flask(__name__)
app.secret_key = b'132d2dcf59f9604c0b48e4e3a1a1cd19a0abf121b48a4777'
#app.route('/')
def get_page():
return render_template('progress.html')
#app.route('/progress')
def progress():
def generate():
x = 0
while x < 100:
x = x + 10
print(x)
time.sleep(0.2)
yield "data:" + str(x) + "\n\n"
session['result'] = x
print(session['result']) #100
return Response(stream_with_context(generate()), mimetype= 'text/event-stream')
#app.route('/done')
def done():
print(session['result']) #KeyError: 'result'
return session['result']
if __name__ == '__main__':
app.run(debug=True)
progress.html:
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script>
var source = new EventSource("/progress");
source.onmessage = function(event) {
$('.progress-bar').css('width', event.data+'%').attr('aria-valuenow', event.data);
if (event.data >=100) {
source.close();
window.location = "http://127.0.0.1:5000/done"
}
}
</script>
</head>
<body>
<div class="progress" style="width: 50%; margin: 50px;">
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
</div>
</div>
</body>
</html>
The session is a cookie. Cookies are sent as headers. Headers are sent first in a response, before the rest of the stream is sent. You can't modify the session after beginning the response. Send whatever data you need to in the stream, and handle it on the receiving end.