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 a base.html template and several child templates
  • a static folder with a style.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:

flask starter page

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.