Flask

In this reading, we'll get you started learning about Flask, a web application framework. You'll also learn about using virtualenv, a very important tool for working with Python.

Web Application Frameworks

Why use Flask or any Web Application Framework versus coding in plain Python? In general, why use a web application framework?

A web application framework systematizes some important but routine aspects of web applications. Most modern web applications share the following features:

  • They use some kind of templating system for creating the responses. This allows separating the computed, varying parts of the response from the static HTML and other front-end stuff. (These are sometimes called the views; see below.)
  • They handle a number of different use cases (ways that the app is used) in a single program.
  • Often, these different use cases are accessed via different URLs. For example, the IMDB has one URL for each person and one for each movie, but of course these are generated by software when requested. In general a web application comprises a set of URLs. The web framework has just one main back-end script (plus, of course, supporting modules and files).
  • The different use cases are distinguished by some initial logic that analyzes the incoming request and decides how to handle it. A web framework centralizes that logic. This logic is also called routing. (It is also sometimes called the controller; see below.)

Frameworks usually involve a long-term process (in the sense of an operating system process). That process lasts across many requests, so templates don't have to be re-read from disk each time, and other persistent information can be retained in memory.

Model-View-Controller

Web frameworks are often described as fitting into the Model-View-Controller (MVC) paradigm. In that analysis:

  • The database is the model.
  • The logic to determine the use case and how to handle it is the controller.
  • The routes and templates that determine what the user sees are called the views.

This terminology is helpful if you already understand MVC, but if you don't, it's useless. Make of it what you will.

Background Concepts

There are a few background concepts that we'll run into when using Flask, so I'd like to describe those first.

Process

A process, in computer science, is a program that is currently executing. Programs that are executing have memory allocated to them that they can store useful stuff in. We all know that if our laptop runs out of battery power or crashes, certain things don't get saved, such as the changes we made to a MS Word document or to a file being edited by Emacs. That's because the changes were in memory but not (yet) saved to disk. (Some applications save to crash recovery files and other kinds of safety nets; don't let those features confuse the issue.)

Some processes run quickly and exit right away, freeing up memory. For example, an ls command runs for less than a second. Other processes stick around for long periods. (I might have Chrome, Firefox, and Emacs all running for weeks on my computer.)

Flask is more like the latter: a long-running process. Indeed, it might run for weeks.

Flask and Apache

Flask is used in two distinct ways:

  • development mode. In development mode, often running just on a single person's laptop, the Flask process is used instead of Apache, in the sense of handling all the web traffic. So, you don't need to have Apache running at all.
  • production mode, also called deployed mode. In this mode, web requests to the server (such as Tempest) go to Apache, which recognizes that they should be handled by your Flask process, and it relays the request to Flask.

Ports

A server computer like Tempest has a number of processes listening to the network. Each process listens on a different port. A port is just a 16-bit number from 1 to 65536. You can think of a port as a numbered door (the French word for door is porte) to the machine. Incoming network packets specify which port they should go to, and then they're handled by the process listening on that port.

Ports numbered less than 1024 are reserved for the operating system. Here are some standard services and the ports they listen on:

  • 22: ssh (this is how you ssh to a machine)
  • 25: sendmail
  • 80: http (Apache)
  • 443: https (Apache using secure connections)
  • 3306: MySQLd (the server)

There are many others; it's not important to remember any of these except for port 80.

If Apache is running on Tempest listening on port 80, and you run your Flask application on Tempest, what port is your Flask application running on? It can't listen on port 80 without interfering with the normal web services that Tempest provides. (You wouldn't be able to open port 80 anyhow, since it's a special port number.)

By default, Flask runs on port 5000 in development mode. That works fine if you're running on your own laptop. But if all of the students in CS 304 are running their Flask applications on Tempest, we can't all use port 5000 without all of us intefering with each other.

Consequently, we all need a way to open a port on Tempest that is unique to us. Fortunately, each user on a Unix system has a user id (UID) as well as a username. The UID is a unique number that will be perfect to keep us all separate. (If you're curious, you can find out your UID by logging in and using the id command.) We'll write our Flask programs to open up a port number corresponding to our UID. So if your UID is 6543, you'll open port 6543.

When using a web browser, if you want to send your web requests to a port other than the default HTTP port (80), simply add a colon and the port number to the URL. For example, when you're developing with Flask on your own machine, you might use a URL like this:

http://localhost:5000/hello

The Flask documentation will have examples like that a lot.

When we develop our Flask apps, instead of localhost and port 5000, we'll each use URLs like this, where the 6543 is our UID and hence our port:

http://cs.wellesley.edu:6543/hello

The CS 304 course account has UID 1942, so when I login to the CS 304 course account and run my Flask app in development mode, it opens port 1942 and I access it at:

http://cs.wellesley.edu:1942/hello

This will be tricky for the first day, and then it'll become completely routine.

VPN and SSH Tunnel

The previous section works great in a normal semester when we are all on-campus. In Fall 2020, none of us is on-campus, so we will encounter a problem. That's because the campus firewall only allows a handful of ports to be open "through" the firewall (ports 22, 80, and 443 for example, but not 1942).

There are two choices we have:

  • Use the VPN. That changes your laptop to be inside the campus firewall, and you can access your app on the port with your UID, as described above. This is relatively easy and works. It can be a little slow (traffic goes back and forth to campus extra times because of the VPN), but I've never really noticed much lag. On the other hand, I live 3 miles from campus, not on the other side of the world.
  • Use an SSH tunnel. The SSH program can set up a "tunnel" through the firewall, relaying traffic to port XYZ on Tempest to port ABC on your laptop.

The SSH incantation to set up a relay from 1942 on tempest to 8080 on my laptop is the following command, which I run in a terminal on my laptop, not on tempest:

ssh -L 8080:localhost:1942 anderson@cs.wellesley.edu

That logs me into my personal account (which I then ignore) and also sets up the SSH tunnel. I then go to my laptop's browser and access this URL:

localhost:8080

(8080, like 5000 and 8000, is commonly used for this purpose, but the number doesn't matter. But there's also no reason to get creative.)

This is described in our FAQ as well: Flask from off-campus

I use SSH tunneling most of the time. I've even written a shell alias so that I don't have to remember the incantation. Ask me how if you're curious.

Flask

Since Flask is a common, well-used web framework, good documentation already exists for it. To prepare for class, please read the following tutorials. (The first links to the next and so forth.)

The Flask Environment page has a small error in it. To activate the virtual environment, do

source venv/bin/activate

Yes, this is similar to the source command in MySQL. Both mean "read code from the specified file". Here, we are reading shell commands, not SQL statements.

Flask Summary

There is a bit more after this summary, so don't stop reading yet.

Here's a summary/recap of the external readings. This section won't substitute for them, but might make a useful refresher when you're looking for some information. (You should not feel obliged to memorize anything in this course.)

  • Flask is a web application (micro) framework.
  • It uses Jinja2 as its templating engine, to combine dynamic data and static templates into dynamically built web pages.
  • Flask works with both Python 2.7 and Python 3. We will be using Python 3 in CS 304.

Virtualenv

Virtualenv is an important general-purpose Python tool, that is not part of Flask or even related to Flask. It has to do with Python coding in a modern environment. I have a separate reading on virtualenv. You must also read that.

Here's a relevant summary about virtualenv in the context of Flask:

  • We will use virtualenv so that you can install Python packages if needed.
  • Virtualenv install Python packages from the web into a folder in your account, so they are separate from anyone else's. Indeed, you could have several virtualenvs, for different projects and such.
  • Virtualenv is already installed on Tempest; no need to install the command.
  • You create (once) a virtualenv by doing virtualenv venv; that creates a directory called venv and puts some initial stuff in it.
  • Whenever you want to use that virtualenv, you do source venv/bin/activate
  • Then you can install flask (once) by doing pip install flask

Basic App

Here is a "hello world" Flask app. You can download it from hello.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

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)

That file is a little different from the TutorialsPoint example, because there's some Python code to determine your UID (a number) and open that port. It also opens an external network inteface (so you can connect from outside Tempest) and turns on debug mode.

Routing

Routing is the mapping of URLs to Python functions. The user clicks a link, browser sends the URL, Flask's routing runs your function. Thus, the user runs your Flask function. It's like magic.

The example above routes the shortest possible URL, namely / to your hello_world function.

If we also want to say a greeting if the user sends the /hello URL, we can do this:

@app.route('/hello')
def greeting():
    # is this too pompous?
    return 'Greetings and Felicitations' 

Variable Rules

All of the preceding had one URL mapping to one function. What if you want a set of URLS, based on some pattern to go to a function? In fact, Flask will also parse the URL according to the pattern, giving you the pieces as arguments to your function.

The example in the reading is good. You can append your name to the URL. Here's the code:

@app.route('/greetme/<name>')
def custom_greeting(name):
    return 'Hello {}, nice to meet you!'.format(name)

The code above uses the Python .format method. See references like GeeksforGeeks and W3Schools

If my browser sends the following URL to Flask:

/greetme/Scott

It will get back (and display in the browser):

Hello Scott, nice to meet you!

Canonical URLS

If you define a route like this, without a trailing slash:

@app.route('/hello')
def greeting():
    return 'Greetings and Felicitations' 

Flask will map /hello and only that to your function. If the browser sends a URL with a trailing slash, /hello/, it gets a 404.

But if you define a route with a trailing slash:

@app.route('/hello/')
def greeting():
    return 'Greetings and Felicitations' 

Then both URLS, with and without the trailing slash, map to that function.

So, generally, prefer a trailing slash.

Templates

We will talk more about true templates in a later class, but for now, we'll just get started by talking about returning an HTML page from a route/handler. (The URL is typically referred to as a route and the function as a handler, since it handles those requests).

The example we've been using is fine, but it doesn't have any HTML in it. But the response is received by the browser and shown to the user, so it could have HTML and such in it:

@app.route('/hello/')
def greeting():
    # definitely too pompous
    return '''<h1>Hail</h1>
              <p>Greetings and Felicitations, <em>honored</em> guest!</p>'''

However, such an approach doesn't scale. It clutters up our Python files with a bunch of HTML (and maybe CSS and JavaScript), much of which is static and probably should be shared among different routes. We will address all those issues when we get to true templating. Meanwhile, we'll talk about how to hide all that HTML in a separate file.

A Flask app calls these templates and puts them in a subfolder of the app called templates. Let's put our app in a folder called greeting_app. Our Flask application now looks like:

greeting_app/
    app.py
    templates/
        greet.html

The greet.html file is an ordinary HTML file. Later, we'll see how to make it dynamic. For now, it's purely static. The interesting part of our app.py now becomes:

from flask import (Flask, render_template)
app = Flask(__name__)

@app.route('/')
def hello_world():
    return render_template('greet.html')

Notice two small changes. The handler has changed so that instead of returning some HTML directly, it returns the value of the render_template function and refers to a file in the templates folder. The second change is that we have to import that function from the flask module (see line 1).

Our templates/greet.html is just plain HTML:

<!doctype html>
<head>
    <meta charset="utf-8">
    <!-- for mobile-friendly pages -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name=author content="Scott D. Anderson">
    <meta name=description content="">
    <meta name=keywords content="">
    <title>Greeting</title>
    <style>
      h1 { color: purple; }
    </style>
</head>
<body>

<h1>Hail</h1>

<!-- definitely *way* too pompous -->

<p>Greetings and Felicitations, <em>honored</em> guest!</p>

</body>
</html>

That's all for now. You know all you need to know for the first flask assignment: forms

Decorators

Flask makes extensive use of Python's decorator syntax. You can ignore the theory and just copy the syntax, but if you're curious, this Primer on Python Decorators is excellent.