
Welcome!
Everything is fine.
Our imports from Flask have been steadily growing and they'll grow a
bit more as the semester continues. There are also some
initializations, such as app.secret_key
and dbi.cache_cnf()
, that
we need to remember to do. There's also the structure of the app, with
sub-folders for templates
and static
. It all gets to be a bit
much.
Indeed, one reason I was initially reluctant to commit to Flask is that I worried that it would be hard to remember all these pieces, and I haven't been entirely wrong.
Of course, once you've done this once, you can just cp -r
some
working app to be a starting point for another app. Or, we can clone
my git repo.
To make this easier, I've created a "starter" app for just that purpose. It has:
- a
templates
folder with abase.html
template and several child templates - a
static
folder with astyle.css
file - an
app.py
file that includes our boilerplate Flask app code, plus some initialization and example routes.
The base.html
template has some Flask/Jinja2 code to show flashed
messages in a default way.
The base.html
template also has an example of the HTML for a simple
a navbar. The CSS file makes it looks a little nicer, while still
being fairly simple.
Here's a screenshot of what starter app looks like, including the navbar:
Here's the HTML code for that page. Notice the use of url_for()
in
loading the CSS and creating the navbar.
<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<!-- for mobile-friendly pages -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name=author content="">
<title>{{ page_title }}</title>
<link rel='stylesheet' href="{{url_for('static', filename = 'style.css')}}">
{% block head_stuff %} {% endblock %}
</head>
<body>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div id="messages">
{% for msg in messages %}
<p>{{msg}}</p>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% block nav %}
<nav>
<ul>
<li><a href="{{url_for('index')}}">home</a></li>
<li><a href="{{url_for('greet')}}">greet</a></li>
<li><a href="{{url_for('testform')}}">testform</a></li>
</ul>
</nav>
{% endblock %}
{% block main_content %}
<h1>Welcome!</h1>
{% endblock %}
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
{% block end_scripts %}
{% endblock %}
</body>
</html>
Here's the code in app.py
that makes it work:
from flask import (Flask, render_template, make_response, url_for, request,
redirect, flash, session, send_from_directory, jsonify)
from werkzeug.utils import secure_filename
app = Flask(__name__)
# one or the other of these. Defaults to MySQL (PyMySQL)
# change comment characters to switch to SQLite
import cs304dbi as dbi
# import cs304dbi_sqlite3 as dbi
import random
app.secret_key = 'your secret here'
# replace that with a random key
app.secret_key = ''.join([ random.choice(('ABCDEFGHIJKLMNOPQRSTUVXYZ' +
'abcdefghijklmnopqrstuvxyz' +
'0123456789'))
for i in range(20) ])
# This gets us better error messages for certain common request errors
app.config['TRAP_BAD_REQUEST_ERRORS'] = True
@app.route('/')
def index():
return render_template('main.html',title='Hello')
@app.route('/greet/', methods=["GET", "POST"])
def greet():
if request.method == 'GET':
return render_template('greet.html', title='Customized Greeting')
else:
try:
username = request.form['username'] # throws error if there's trouble
flash('form submission successful')
return render_template('greet.html',
title='Welcome '+username,
name=username)
except Exception as err:
flash('form submission error'+str(err))
return redirect( url_for('index') )
@app.route('/formecho/', methods=['GET','POST'])
def formecho():
if request.method == 'GET':
return render_template('form_data.html',
method=request.method,
form_data=request.args)
elif request.method == 'POST':
return render_template('form_data.html',
method=request.method,
form_data=request.form)
else:
# maybe PUT?
return render_template('form_data.html',
method=request.method,
form_data={})
@app.route('/testform/')
def testform():
# these forms go to the formecho route
return render_template('testform.html')
@app.before_first_request
def init_db():
dbi.cache_cnf()
# set this local variable to 'wmdb' or your personal or team db
db_to_use = 'put_database_name_here_db'
dbi.use(db_to_use)
print('will connect to {}'.format(db_to_use))
if __name__ == '__main__':
import sys, os
if len(sys.argv) > 1:
# arg, if any, is the desired port number
port = int(sys.argv[1])
assert(port>1024)
else:
port = os.getuid()
app.debug = True
app.run('0.0.0.0',port)
If you know and prefer Bootstrap or any other way of doing your website, you are welcome to rip out that navbar and replace it with your own. If you are new to HTML and CSS and want to just use this one, you are welcome to do so.
Your project app is yours. It need not work or look like mine.