This question already has an answer here:
Flask : href link to html not working
(1 answer)
Closed last year.
I'm trying to make a program to display some time and temperature numbers on a web page. I have two html templates, "index.html" and "set.html" in a "templates" directory under the main "furnace" directory. The index part works as it should when called by flask but the "set" link (to go to a page to enter a new time and date) results in a "404 Not found" error. I've stripped everything out of the Python program that isn't part of flask and displaying the html, and I still get the error. At this point, all that's left is about the same as simple examples I've found; I've compared my program to the examples and I just can't see what's going wrong. Can some sharp eyes spot my mistake?
furnace-9x.py
import time
import datetime
import RPi.GPIO as GPIO
import smbus
from gpiozero import Button
from flask import Flask, render_template
import SDL_DS3231_dev
import lcddriver
import threading
# Using a global here for simplicity in this demo
# Globals are generally bad practice, so you would maintain
# the status of "lcd_counter" in a more elegant way
# (e.g. through a shared module, through classes, etc.)
# A simple global serves the purpose for this demo
# of a flask server running in parallel to another process
# emulating some I/O, printing to the console, etc.
lcd_counter = 0
app = Flask(__name__)
#app.route("/")
def index():
print('Index')
global datetimestr, temperature
global minutes_24,minutes_30,heatOn
return render_template('index.html')
#app.route("/set")
def setTime():
print("Set time")
return render_template('set.html')
if __name__ == '__main__':
#=== Prelude ===
app.run(debug=False, host='0.0.0.0')
index.html
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body {font-family:arial;
background-color: rgb(185,179,175);
text-align: left;}
table, tr {text-align: left;
font-size: 150%;
border-style: solid;}
</style>
</head>
<body>
<h1>Furnace</h1>
<table>
<tr>
<td colspan="3">{{dtstr}}</td>
</tr>
<tr>
<td>30 day: </td>
<td>{{m30}}</td>
<td>{{p30}}%</td>
</tr>
<tr>
<td>24 hour: </td>
<td>{{m24}}</td>
<td>{{p24}}%</td>
</tr>
<tr>
<td>Temp:</td>
<td> {{temper}}°F </td>
<td>Heat: {{heat}}</td>
</tr>
</table>
<p>Set time</p>
</body>
</html>
set.html
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body {font-family:arial;
background-color: rgb(185,179,175);
text-align: left;}
table, tr {text-align: left;
font-size: 150%;
border-style: solid;}
</style>
</head>
<body>
<p>Enter the date and time:
<input type="text" name="datetime" size="15" maxlength="19" />
<input type="submit" value="Submit" />
</p>
</body>
</html>
The problem here is a mismatch between the link in index.html (set.html) and your route (/set).
To resolve the issue, you can update line 35 in your index.html like this:
<p>Set time</p>
The flask web server matches the route strings exactly, and not the file names of the templates. Even better, you can use the flask built in function url_for:
<p>Set time</p>
Related
I am trying to create web application using Flask and python. I created frames in the html page by using iframe. So, I have two sections in the HTML Page now. In the top iframe, I have few input fields and submit button. And the bottom iframe, I have a blank page. So, when I hit submit button which is in top iframe, the results should be displayed in the bottom iframe. I can get results in another html page which I do not want it.
How do I get results on bottom iframe in the same html page?
Query.py:
from flask import Flask, render_template, request, redirect, url_for
import sys
import logging
app = Flask(__name__)
# Ensure that debug mode is *on*
app.debug = True
#app.route('/')
def query():
return render_template('query.html')
#app.route('/queryform')
def queryform():
return render_template('queryform.html')
#app.route('/queryform', methods=['POST'])
def queryform123():
data = request.form['f1']
dselection = request.form['dbselection']
e = mqr.a(data)
return render_template('query.html', s = e, p = dselection)
#app.route('/queryblank')
def queryblank():
return render_template('queryblank.html')
if __name__ == '__main__':
app.jinja_env.auto_reload = True
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.run(debug=True, port='5001')
Query.html
<html>
<head>
<title>Query</title>
<style>
.menu {
float:right;
width:85%;
height:35%;
}
.mainContent {
float:right;
width:85%;
height:65%;
}
</style>
</head>
<body>
<iframe class="menu" id="frame1" src="queryform"></iframe> //-- this is html page
<iframe class="mainContent" id="frame2" src="queryblank"> </iframe> //--this is another html page
</body>
</html>
Queryblank.html
<html>
<head>
<title>queryblank</title>
</head>
<body>
<center>
Query Results Window
</center>
</body>
</html>
Queryform.html
<html>
<head>
<title> query form</title>
</head>
<body>
<table width="100%" height="25">
<tr>
<td width="100%" bgcolor="#0000FF" align="center" color="#000000">
<font face="Arial Black" color="#FFFFFF">Query</font></td>
</tr>
</table>
<form name="queryform" target="bottom" method="POST">
<input type=hidden name="butn">
<td><input type=text name="f1"></td>
</table>
Database:
<SELECT NAME=dbselection>
<OPTION SELECTED>Dev
<OPTION>Prod
</SELECT>
<input type="submit" name="submit_button" value="Query Database">
</form>
</body>
</html>
I have 2 scripts. One is my main.py script where it will call my other script to print an HTML page. The second script is html_pages.py. I'm trying to print an HTML page by importing html_pages.py and calling the welcome_page or login_page function.
Whenever I try to reach the page it says "Internal error". This was working previously when I had the login_page and welcome_page as a string stored in the main script. But it doesn't work when I try to call the function from a different script.
my main.py script contains:
#!/usr/bin/python3
import html_pages
if "HTTP_COOKIE" in os.environ :
cookie_info = os.environ["HTTP_COOKIE"]
cookies = cookie_info.split(';')
for cookie in cookies:
cookie_split = cookie.split('=')
cookie_dict[cookie_split[0].strip()] = cookie_split[1].strip()
CookieUsername = cookie_dict.get('username')
CookiePassword = cookie_dict.get('password')
CookieToken = cookie_dict.get('CSRFtoken')
#Connect to the database
import pymysql
conn = pymysql.connect(db='project2', user='algarcia1', passwd='root', host='localhost')
c = conn.cursor()
#Collect info about the user
query = "SELECT * FROM bank WHERE username='{CookieUsername}'"
c.execute(query.format(CookieUsername=CookieUsername))
conn.commit()
user = c.fetchone()
print(html_pages.welcome_page(user[0],user[3],user[4]))
else:
cookie_dict["username"] = "undefined"
cookie_dict["password"] = "undefined"
print(html_pages.login_page())
My HTML_pages.py script looks like this:
#!/usr/bin/python3
#Create a login HTML page
def login_page(status):
loginpage = """Content-Type: text/html
<!DOCTYPE html>
<!-- HTML code to send a POST request to login.py -->
<html>
<head>
<title>Safe Bank Website</title>
</head>
<body>
<form action="login.py" method="POST">
<h1>Safe Bank Website</h1>
<strong>Username:</strong><br>
<input type="text" name="username"><br>
<strong>Password:</strong><br>
<input type="text" name="password"><br>
<strong>CSRF token:</strong><br>
<input type="text" name="CSRFtoken"><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>"""
print(login_page.format())
def welcome_page(username, chequings, savings):
#Create a welcome HTML page
welcomepage = """Content-Type: text/html
<!DOCTYPE html>
<html>
<head>
<style>
table, th, td {{ border: 2px solid black; text-align: left;}}
th, td {{ padding: 5px; }}
</style>
</head>
<body>
<h2>Welcome {cookie_info}!</h2>
<table style="width:100%">
<tr>
<th>Chequings</th>
<th>Savings</th>
</tr>
<tr>
<td>{chequings}</td>
<td>{savings}</td>
</tr>
</table>
Transfer money
</body>
</html>"""
print(welcome_page.format(cookie_info=username,chequings=chequings,savings=savings))
There are so many good python Web-Frameworks like Flask or Django. That make it easy so build webpages in a secure way. Especially Flask is very easy to use. Try them out and look how such frameworks do it.
In most cases it's more solid for future use to use a framework where you have a good structure.
i came across get_template_attribute in the flask api and thought of it as a great way to achieve partial page rendering.
so the goal was
create a 2 layer menu-page layout
menu should be static
on click of
menu, page should be shown dynamically without refreshing the entire
page
so i did something like so
LandingPage.html
<html>
<head>
<style>
html,body{
height:100%;
}
</style>
<script type="text/javascript">
function finalConfirmationToStartJob() {
alert('in here')
$.ajax({
type : 'GET',
url : 'home2',
data : ''
});
};
</script></head>
<body>
<table border=1 height=100% width=100%>
<tr><td colspan='2'/></tr>
<tr>
<td width=15%>
<ul>
<li>ABC</li>
<ul>
<li>ABC1</li>
<li>ABC2</li>
<li>ABC3</li>
</ul>
<li>DEF</li>
<li>GHI</li>
<li>JKL</li>
</ul>
</td>
<td>
{% macro hello(name) %}Hello {{ name }}!{% endmacro %}
how are you darlings!!! {{hello()}}
</td>
</tr>
<tr><td colspan='2'/></tr>
</table>
</body>
</html>
Controller
#app.route("/home")
def goto_home():
return render_template('/LandingPage.html')
#app.route("/home2")
def try_out_macro():
print 'hellooo elloo'
hello = get_template_attribute('/LandingPage.html', 'hello')
return hello('World')
However when i do click on the action link, even though the controller actually returns perfect data , the data is not displayed on the screen.
When i hit the url /home2 i do get perfect data, however on click on action link nothing happens on the html
Can someone please help me, i am not sure what i am doing wrong
PS : i got it to work using innr html and div combination, however i would still like to know why the above doesnt work.
I’m still relatively new to Flask, and a bit of a web noob in general, but I’ve had some good results so far. Right now I’ve got a form in which users enter a query, which is given to a function that can take anywhere between 5 and 30 seconds to return a result (looking up data with the Freebase API).
The problem is that I can’t let the user know that their query is loading during this time, as the results page only loads once the function finishes its work. Is there a way I can display a loading message while that's going on? I found some Javascript that could display a loading message while page elements are still loading, but my waiting period happens before ‘render_template’.
I knocked together some example code, just to demonstrate my situation:
Python:
from flask import Flask
from flask import request
from flask import render_template
import time
app = Flask(__name__)
def long_load(typeback):
time.sleep(5) #just simulating the waiting period
return "You typed: %s" % typeback
#app.route('/')
def home():
return render_template("index.html")
#app.route('/', methods=['POST'])
def form(display=None):
query = request.form['anything']
outcome = long_load(query)
return render_template("done.html", display=outcome)
if __name__ == '__main__':
#app.debug = True
app.run()
Excerpt from index.html:
<body>
<h3>Type anything:</h3>
<p>
<form action="." method="POST">
<input type="text" name="anything" placeholder="Type anything here">
<input type="submit" name="anything_submit" value="Submit">
</form>
</p>
</body>
Excerpt from done.html:
<body>
<h3>Results:</h3>
<p>
{{ display }}
</p>
</body>
Any help would be greatly appreciated, I hope this example helps.
Add this to your index.html or js file (I'm assuming you have jQuery here, you could use standard javascript of course.):
<script type="text/javascript">// <![CDATA[
function loading(){
$("#loading").show();
$("#content").hide();
}
// ]]></script>
Add this to you html or css file:
div#loading {
width: 35px;
height: 35px;
display: none;
background: url(/static/loadingimage.gif) no-repeat;
cursor: wait;
}
You can get an adequate GIF from http://www.ajaxload.info/. Download and put it into your static folder.
Then change your submission button to call above js function:
<input type="submit" name="anything_submit" value="Submit" onclick="loading();">
and add in a loading and a content div to you base html file:
<body>
<div id="loading"></div>
<div id="content">
<h3>Type anything:</h3>
<p>
<form action="." method="POST">
<input type="text" name="anything" placeholder="Type anything here">
<input type="submit" name="anything_submit" value="Submit" onclick="loading();">
</form>
</p>
</div>
</body>
Now when you click 'Submit', the js function should hide your content and display a loading GIF. This will display until your data is processed and flask loads the new page.
This can be done by using a div that contains a 'loading gif' image. When the submit button is clicked, the div is displayed using javascript.
To implement this, you can take a look at this website: http://web.archive.org/web/20181023063601/http://www.netavatar.co.in/2011/05/31/how-to-show-a-loading-gif-image-while-a-page-loads-using-javascript-and-css/
I found the purely CSS-dependent loader very useful. It does not depend on external resources:
https://www.w3schools.com/howto/howto_css_loader.asp
This is a bit of an old topic, but I needed to deal with this problem today and came with a solution on my own. I'm running a machine learning model that recieves an image input from the user and does some magic.
Basically this is what I did.
On my index.html file, I called a loading function on my "Submit" button passing the filename, because I was going to use it later:
<form method="post" action="/loading/{{filename}}"
enctype="multipart/form-data">
<input type="submit" value="Submit!">
</form>
On Flask, I created a route just to render the loading screen before doing the time consuming task, also passing the filename ahead:
#app.route('/loading/<filename>', methods=['POST'])
def loading_model(filename):
return render_template ("loading.html", filename=filename)
And then, on loading.html, I render my .gif animation and at the end I redirect the page to the time consuming task function:
<!doctype html>
<head>
<link rel= "stylesheet" type= "text/css" href= "{{url_for('static',filename='styles/main.css') }}">
</head>
<div id="preloader">
<div id="status"> </div>
<h1 class="ml13">ANALYZING...</h1>
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js> </script>
</div>
<title>Title</title>
<script src="{{url_for('static', filename='main.js')}}"></script>
<script> window.location.replace('/task/{{filename}}'); </script>
And then, final step, back to Flask, call the task function:
#app.route('/task/<filename>', methods=['POST', 'GET'])
def task(filename):
# Do your stuff
return render_template ("results.html")
By doing this, the gif animation will keep playing whilst the function does its job, and then render the results or the next page you want.
You obviously have to edit the css file so that "preloader" and "status" behave like you wish, this is how I used it:
#preloader {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
#status {
background-image: url("lalala.gif");
background-repeat: no-repeat;
width: 800px;
height: 600px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -400px;
margin-left: -400px;
}
It worked out for me.
Brilliant #jka.ne but confusing situation.
I only needed to introduce the loading gif while a button was clicked.
My solution was:
<script type="text/javascript">
function loading(){
$("#loading").show();
window.location.href="../target_html";
}
</script>
Then:
<button type="button" class="xxx" onclick="loading();">Run</button>
Finally:
<div id="loading"></div>
I have tried reading the docs for Bottle, however, I am still unsure about how static file serving works. I have an index.tpl file, and within it it has a css file attached to it, and it works. However, I was reading that Bottle does not automatically serve css files, which can't be true if the page loads correctly.
I have, however, run into speed issues when requesting the page. Is that because I didn't use the return static_file(params go here)? If someone could clear up how they work, and how they are used when loading the page, it would be great.
Server code:
from Bottle import route,run,template,request,static_file
#route('/')
def home():
return template('Templates/index',name=request.environ.get('REMOTE_ADDR'))
run(host='Work-PC',port=9999,debug=True)
Index:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type"
content="text/html; charset=ISO-8859-1">
<title>index</title>
<link type="text/css"
href="cssfiles/mainpagecss.css"
rel="stylesheet">
</head>
<body>
<table
style="width: 100%; text-align: left; margin-left: auto; margin-right: auto;"
border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td>
<h1><span class="headertext">
<center>Network
Website</center>
</span></h1>
</td>
</tr>
</tbody>
</table>
%if name!='none':
<p align="right">signed in as: {{name}}</p>
%else:
pass
%end
<br>
<table style="text-align: left; width: 100%;" border="0" cellpadding="2"
cellspacing="2">
<tbody>
<tr>
<td>
<table style="text-align: left; width: 100%;" border="0"
cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="width: 15%; vertical-align: top;">
<table style="text-align: left; width: 100%;" border="1"
cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td>Home<br>
<span class="important">Teamspeak Download</span><br>
<span class="important">Teamspeak Information</span></td>
</tr>
</tbody>
</table>
</td>
<td style="vertical-align: top;">
<table style="text-align: left; width: 100%;" border="1"
cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td>
<h1><span style="font-weight: bold;">Network Website</span></h1>
To find all of the needed information relating to the network's social
capabilities, please refer to the links in the side bar.</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</body>
</html>
To serve static files using bottle you'll need to use the provided static_file function and add a few additional routes. The following routes direct the static file requests and ensure that only files with the correct file extension are accessed.
from bottle import get, static_file
# Static Routes
#get("/static/css/<filepath:re:.*\.css>")
def css(filepath):
return static_file(filepath, root="static/css")
#get("/static/font/<filepath:re:.*\.(eot|otf|svg|ttf|woff|woff2?)>")
def font(filepath):
return static_file(filepath, root="static/font")
#get("/static/img/<filepath:re:.*\.(jpg|png|gif|ico|svg)>")
def img(filepath):
return static_file(filepath, root="static/img")
#get("/static/js/<filepath:re:.*\.js>")
def js(filepath):
return static_file(filepath, root="static/js")
Now in your html, you can reference a file like so:
<link type="text/css" href="/static/css/main.css" rel="stylesheet">
Directory layout:
`--static
| `--css
| `--fonts
| `--img
| `--js
Just providing an answer here because a number of my students have used this code in an assignment and I have a bit of a concern about the solution.
The standard way to serve static files in Bottle is in the documentation:
from bottle import static_file
#route('/static/<filepath:path>')
def server_static(filepath):
return static_file(filepath, root='/path/to/your/static/files')
in this way, all of the files below your static folder are served from a URL starting with /static. In your HTML, you need to reference the full URL path for the resource, eg:
<link rel='stylesheet' type='text/css' href='/static/css/style.css'>
The answer from Sanketh makes it so that any reference to an image, css file etc anywhere in the URL space is served from a given folder inside the static folder. So /foo/bar/baz/picture.jpg and /picture.jpg would both be served from static/images/picture.jpg. This means that you don't need to worry about getting the path right in your HTML code and you can always use relative filenames (ie. just src="picture.jpg").
The problem with this approach comes when you try to deploy your application. In a production environment you want the static resources to be served by a web server like nginx, not by your Bottle application. To enable this, they should all be served from a single part of the URL space, eg. /static. If your code is littered with relative filenames, it won't translate easily to this model.
So, I'd advise using the three line solution from the Bottle tutorial rather than the more complex solution listed on this page. It's simpler code (so less likely to be buggy) and it allows you to seamlessly move to a production environment without code changes.
As indicated in the documentation, you should serve static files using the static function and css is a static file. The static function handles security and some other function which you can find out from the source. The path argument to the static function should point to the directory wherever you store the css files
Rather than use regular expression matching for serving files as in Sanketh's answer, I'd prefer not to modify my templates and provide a path to the static files explicitly, as in:
<script src="{{ get_url('static', filename='js/bootstrap.min.js') }}"></script>
You can do this simply by replacing the <filename> in the static route decorator with one of type :path - like so:
#app.route('/static/<filename:path>', name='static')
def serve_static(filename):
return static_file(filename, root=config.STATIC_PATH)
The :path matches an entire file path in a non-greedy way so you don't have to worry about changing templates when switching to production - just keep everything in the same relative folder structure.
I've used Sanketh's template in the past but over time condensed it to an extension agnostic function. You just have to add extension-folder mappings in the ext_map dictionary. It defaults to static/ folder if an extension is not mapped explicitly.
import os.path
# Static Routes
#get('/<filename>')
def serve_static_file(filename):
ext = os.path.splitext(filename)[1][1:]
ext_map = {'image':['png','gif','jpg','ico'],'js':['js']}
sub_folder = next((k for k, v in ext_map.items() if ext in v),'')
return static_file(filename, root='static/'+sub_folder)