Python Flask Web App – Times Tables Quiz

Have you ever built a web app? Me neither! That’s why I’m going to build this app so we can both learn a thing or two. The components I will use are:

  • Python Flask Web Framework
  • HTML
  • No CSS!
  • No Bootstrap!
  • No React!
  • No Javascript!
  • No SQL!

So basically just python, Flask and HTML. I will also impose these limitations to make things simple and interesting:

  • No external HTML files – all HTML written within python files!
  • No http server in front of the Flask development http server – no NGINX, no Apache, no nothing! Just the Flask development http server.

This app will violate so many best practices, I can’t count. You have been warned.

You can find the entire code in one file on my Github account:

https://github.com/jamesmcclay/python_flask_times_tables

Install Flask

Make sure you install Flask, this is what we’re going to use for the main logic of the app. You might have to type “python3” or “python”, depending on what kind of system you have and how python is installed.

python3 -m pip install flask

Good to go!

Create the first page

I use vim, because using PyCharm or VisualStudio Code would probably be a lot more productive.

At the top of our file, I’ll do some imports to pull Flask in:

from flask import Flask
from flask import request

Then we’ll create the application object, and put it in a variable called app. I don’t actually know what this does other than fire up Flask. We’ll use the app object at the bottom of the file to actually start the http server and Flask app:

app = Flask(__name__)

I’m going to show the html in a separate block here so you can see it in html highlighting. Then I’ll show it embedded in the python code. It’s hard to look at in the python code because it’s all highlighted as a string.

My html code creates a form, asking the user to submit their requested times table (2*X table, 3*X table, etc). The selected table (it’s just a number) will be stored in the query string that is sent back to a different URL at /next.

<html>
<form action ="/next" method="get">
<label for="times_table">What times table do you want?</label><br>
<input type="text" id="times_table" name="times_table"><br> <!-- the variable "times_table" stores the requested table -->
<input type="submit" value="Submit">
</form>
</html>

Now here’s that html embedded into python code, in a multi-line string:

@app.route('/') #Flask provides this. This function will respond to requests to '/'
def index():
    #Three quotes makes a multi-line string
    form = '''
    <html>
    <form action ="/next" method="get">
    <label for="times_table">What times table do you want?</label><br>
    <input type="text" id="times_table" name="times_table"><br>
    <input type="submit" value="Submit">
    </form>
    </html>
    '''
    return form

Flask gives me a ‘decorator’, a type of python magic that allows you to add special functionality to an otherwise normal function. In this case @app.route('/') gives this function the ability to respond to http requests using return. Whatever return sends back will in fact be sent to the user’s browser. In this case, it’s sending back the html form.

At the bottom of the file, we add the command to run the app (it will listen on all network adapters, beware):

app.run(host='0.0.0.0', port=80)

The program should be runnable at this point, let’s give it a shot! This command runs the Flask development http server. I had to put the -E option on sudo to allow python to access all my modules.

sudo -E python3 times_tables_website.py 

Opening up a browser, we can just go to the loopback adapter at 127.0.0.1 and see the app! I zoomed in a bit:

Entering 6 and hitting submit, I will get a 404 because /next hasn’t been implemented yet, but you can see the query string has sent the data we want:

Implementing /next

Here is the html form I’m going to send from the /next route. It will also be embedded in my python string, so I’m showing it once here in html highlighting. I’m using a trick in order to store data in the form I send to the user. I send a couple of hidden <input> labels to store times_table and multiplier. When the user sends the form, my function will be able to extract those and know what question to ask the user next.

<html>
<form method="get">
{judgement} <!-- Not HTML!! Will use python to insert label showing answer correct/not -->
<input type="hidden" id="times_table" name="times_table" value="{times_table}"> <!--store times_table variable hidden-->
<input type="hidden" id="multiplier" name="multiplier" value="{multiplier}"> <!--store multiplier variable hidden-->
<label for="answer">What is {times_table} x {multiplier}</label><br> <!-- python string interpolation again-->
<input type="text" id="answer" name="answer"><br> <!-- answer variable stored here-->
<input type="submit" value="Submit">
</form>
</html>

Next we will implement the /next route. Web developers call these routes, they are relative to the top level of your site which is always /. In this code, we’ll use request.args to get data from the request that was sent. Remember the form put a times_table variable in the query string? We’ll need to get that to know what table the user requested. The way to grab one is with request.args.get('variable_in_query_string'). We’ll also grab the multiplier, but on the first page multiplier won’t exist in the query string because the user hasn’t started yet. Which means the multiplier should start at 1. So if multiplier is None, I set it to 1. Otherwise, grab it, turn it into an int, and do a check to see if the user’s answer is correct. The judgement label will dynamically change based on the answer, and be inserted before being sent back to the user.

@app.route('/next') #Answers requests to the "/next" route
def next_row():
    times_table = request.args.get('times_table') #"request" was brought in from a flask import, lets us get query string variables
    times_table = int(times_table) #turn "times_table" into a number so it can be multiplied
    multiplier = request.args.get('multiplier') #get the multiplier
    answer = request.args.get('answer') #get what user answered. I use int() later because this is None on first page
    if multiplier == None:
        multiplier = 1
        judgement = ''
    else:
        multiplier = int(multiplier) #turn multiplier into a number
        if int(answer) == (times_table * multiplier): #check if answer is correct. I use int() here because this is not None
            judgement = '<label>You got it right!</label><br><br>' #judgement inserted into html (interpolated) later
        else:
            judgement = '<label> You need to practice!!!!!</label><br><br>'
        multiplier +=1 #increment multiplier for the next question

    #This is string interpolation as of python 3.6, using "f" before initial string quote
    form = f''' 
    <html>
    <form method="get">
    {judgement}
    <input type="hidden" id="times_table" name="times_table" value="{times_table}">
    <input type="hidden" id="multiplier" name="multiplier" value="{multiplier}">
    <label for="answer">What is {times_table} x {multiplier}</label><br>
    <input type="text" id="answer" name="answer"><br>
    <input type="submit" value="Submit">
    </form>
    </html>
    '''

    return form

Let’s try and see if it works!

It works! Hope you enjoyed this one. I sure learned quite a bit.

Leave a Reply

Your email address will not be published. Required fields are marked *