Blog Post 3 - Flask Dynamic Web Applications

This blog post will review my creation of a dynamic web application using Flask. You can view my code on Github here. My repo began as a fork of John Zhang’s repo flask-interactions. This gave me a good basis on which to build my database.

Flask Backend

I used Flask to control most of the interactions of the application. The functions draw heavily from John Zhang’s auth_db. Additionally, I used the housewares example to learn about using Blueprints in Flask. All of my functions are stored in a new file messages.py which is imported into our base file, __init__.py.


Database Setup/Teardown

The first function is a simple connection function which returns the database connection once established.

def get_message_db():
    # Check if message_db is in global var
    if 'message_db' not in g:
        # connect to the database and run a script to add the messages table if necessary
        g.message_db = sqlite3.connect('message.sqlite')
        with current_app.open_resource('init.sql') as f:
            g.message_db.executescript(f.read().decode('utf8'))
    return g.message_db

The next function tidies up the database at the shutdown of the site by closing the connection.

def close_message_db(e=None):
    # Close the message_db
    db = g.pop('message_db', None)
    if db is not None:
        db.close()

Submitting messages

Our first new function, insert_message takes a POST request containing a name and a message which is inserted into our messages_db using a simple sql insertion. Then the change is committed to the database.

def insert_message(request):
    # get connection to the database
    db = get_message_db()
    c = db.cursor()

    # retrieve name and message from our request
    name = request.form["name"]
    message = request.form["message"]

    # sql statement (id autoincrements)
    sql = f"""
    INSERT INTO messages (handle, message)
    VALUES ('{name}','{message}');
    """
    c.execute(sql)

    db.commit()

Our survey function renders the submit.html page and directs any POST requests to the insert_message function. Plesae see the HTML section for more details on how this html page works.

@messages_bp.route('/', methods=['POST', 'GET'])
def survey():
    if request.method == 'GET':
         return render_template('submit.html')
    else:
        try:
            insert_message(request)
            return render_template('submit.html')
        except:
            return render_template('submit.html', error=True)

Viewing Messages

Our first function for the viewing tab, random_messages takes a value n and attempts to select n messages from our database. Any failures cause this function to return None which renders in our view.html as a blank page.

def random_messages(n):
    result = []
    db = get_message_db()
    c = db.cursor()
    try: 
        length = c.execute("SELECT COUNT(id) FROM messages").fetchone()[0] + 1 
        results = c.execute(f"SELECT * FROM messages ORDER BY RANDOM() LIMIT {min(n, length, 5)}").fetchall()
    except:
        results = None
    return results

The view function implements the random_messages function. It simply takes results from the function and passes them to the HTML for list.html. See the HTML section for more details on how list.html uses these lists of values.

@messages_bp.route('/view/', methods=['GET']) 
def view():
    try:
        results = random_messages(5)
        if results:
            names = [elem[1] for elem in results]
            messages = [elem[2] for elem in results]
            return render_template('list.html', names=names, messages=messages)
        else:
            return render_template('main_better.html', error=True)
    except:
        print('error in /view/')
        return render_template('main_better.html', error=True)

HTML & CSS Frontend

I used some HTML templates from class and made some slight changes to template CSS to customize the frontend. In order to display this html file I needed to escape the functions using a backslash. Unfortunately the backslashes seem to be displaying still so apologies in advance.

{\% extends 'base.html' %}

{\% block header %}
  <h1>{\% block title %}Messages List{\% endblock %}</h1>
{\% endblock %}

{\% block content %}
  This list contains a bunch of random messages from our database of submitted messages!
  <ul>
  {\%for i in range(0, 5)%}
    <li>{\{messages[i]}}</li>
    <ul>
      <li>{\{names[i]}}</li>  
    </ul>
    <br>
  {\%endfor%}
  </ul>
{\% endblock %}

Simply this page takes in the random lists messages and names and displays them in a simple indented list format.

Results

Our Home page, customized with fonts, colors, and a friendly frog!

Our Ask page, which I kept because it helps fill some space in the header and is a neat demo.

Submit Page

This page shows the submission process for messages and handles.

Viewing Page

This page displays the random messages in our database.

One fun thing is that I learned that, using the flag host with our run command, I could let my friends access my site and submit messages! flask run --host=0.0.0.0

Written on October 27, 2021