This is a very tiny introduction to web application development using the Flask Python “microframework”. A web application is by no means the only, nor the best means of digital publishing. But knowing how to build one can help you think in more data-centric ways, while also forcing you to think about user interfaces and experiences.
In this tutorial, we just get acquainted with the basic conventions of Flask and web application development. This tutorial assumes you know a bit about Python, but virtually nothing else about the web stack, including what HTML or a web server is.
If you are running Anaconda, you shouldn't have to install Flask via pip. But just in case you don't, run this from your shell:
pip install Flask
As with any other library, if it's already installed, you won't break anything (hopefully).
You're going to want to do this with a proper text editor, such as Sublime Text 3 (do not download Sublime Text 2), which comes with a 30-day free period and an occasional (ignorable) reminder afterwards. Or, you could go with Github's free Atom editor.
In this tutorial, we'll be making a one-page web app. If you peruse other Flask apps, such as NICAR's First News App tutorial, you'll see that a web app consists of several files and folders. But right now, we'll just keep things simple with a single file named app.py.
Here's what the code for the finished web app will look like in your text editor:
There's a lot of conventions and structures you probably don't know. So try to find and focus on what you do know. Even if it's just this:
"Hello world!"
The Flask documentation is great; there's even a Quickstart section titled, A Minimal Application. Our tiny Flask app basically follows its lead, with a few modifications that you should be able to understand and tweak yourself.
Web application development is a complicated business, and you will struggle with not just avoiding typical Python programming errors, but also, web application-specific errors. The hardest part, of course, is knowing the difference when you don't know much about Python and virtually nothing about web applications.
My intent is that you can complete the first parts of this tutorial without too much confusion…but I can't predict all the ways you might accidentally break things. So you might have to skip down to the Debugging section at the end of this tutorial if you hit a roadblock.
Let's get started.
The core of a Flask application is a file named app.py; think of it as short for, application.
In your text-editor, create a new file named app.py – and keep track of where you save it, because you will need to be able to activate it from the command-line like so:
python app.py
We'll get to that python-script-running step in a minute. First, we have to write the code for our app.
As with all functionality that goes beyond Python's standard library, we need an import statement to bring in Flask. Add this to the top of app.py:
from flask import Flask
Then, instantiate a Flask
object (technically, a flask.app.Flask
object) and assign it to an easy-to-remember variable; people seem to go for the simple label of: app
:
app = Flask(__name__)
Don't worry about that __name__
object, it's just a convention.
Finally, add this:
if __name__ == '__main__':
app.run()
The above snippet is yet another common Python convention, one that you can get by with just memorizing rather than fully understanding…though at some point, if you're curious:
All together, this is what your app.py file should look like:
from flask import Flask
app = Flask(__name__)
if __name__ == '__main__':
app.run()
Save the file. Then jump into your command-line and change into the directory that app.py exists.
Running your Flask app is no different than running any other Python script. Go to your system shell and run:
python app.py
But unlike your typical Python script, this script, thanks to the Flask library, will start a local webserver. You should see this message (I'll frequently refer to it as a console message):
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Here's an animated version of the process:
What does that mean? It means if you visit that URL in your web browser (127.0.0.1
always refers to your own machine), you'll see your brand new web app!
You might have made an error even before you tried visiting your web app with your browser. Here's a common error after running python app.py
:
python: can't open file 'app.py': [Errno 2] No such file or directory
That error message means exactly what it says: you're trying to run a file named app.py
but it doesn't exist. Which means you either didn't save the file properly. Or you didn't save it with the name app.py
. Or you didn't save it to directory you thought you did. Or, when opening your Terminal and trying to navigate to where you think app.py
exists…you didn't navigate to where you had hoped to be.
Use the standard shell commands to make sure you know where you are (pwd
for Linux/OSX, or the Windows equivalent). And as much as possible use the Tab key to autocomplete, even for something as short as app.py
.
Assuming you've followed my example exactly and haven't made a typo, pop open a new browser window and visit the URL at http://127.0.0.1:5000/.
Or, you might prefer the human-readable equivalent: localhost:5000).
Either one will result in a Not Found error message (aka, the dreaded 404) being displayed in your web browser:
Note: you may get an error message that reads, " Internal Server Error"
You can jump down to the debugging section for a few more details. Or, you can modify your call to app.run()
:
if __name__ == '__main__':
app.run(debug=True)
This will allow the app to display a proper Python error message, so you can fix the typo/syntax error.
Assuming you're at the point where your browser displays the Not Found error…well, your app is broken, and you need to fix it.
Time to abort. Hit Ctrl-C to break out of this unwanted sequence (this, or any other system process):
Note how visiting your web app at 127.0.0.1:5000
resulted in a couple of server messages:
127.0.0.1 - - [04/Apr/2016 18:34:53] "GET / HTTP/1.1" 404 -
127.0.0.1 - - [04/Apr/2016 18:34:53] "GET /favicon.ico HTTP/1.1" 404 -
You're basically a system devops watching traffic hit your webserver…in this case, you're seeing the error messages (note the 404
).
So why did our web app return a 404 page? Because our app literally has no "pages" – you saw the 4 lines of code we've produced so far.
Part of what makes a web app a web app is that it has multiple paths, i.e. URLs. When you visit http://www.nytimes.com/
– i.e. its homepage – the NYT webserver is directing you to what's commonly known as an index page. When you visit the URL http://www.nytimes.com/pages/world
, the pages/world
path let's the webserver know that we want to be sent to wherever the World news section is on the NYT's great big webserver in the sky.
Right now, our web app doesn't include the code that defines even the most basic route, http://127.0.0.0.1/` – which is commonly referred to as the index page or homepage.
Without a single route defined, visiting 127.0.0.1:5000
causes the app to respond…with a "WTF I don't know who you are or what you want".
To specify a route in our web app, we need to do two things:
"/"
and "/photos/cats
, that follows the domain address in the URL, e.g. http://127.0.0.1/
and http://127.0.0.1/photos/cats
To define a path, use this convention (the @
sign is very important here) with the app.route()
method:
@app.route("/")
Ignore the @
convention for now (technically, it's called a decorator) and instead, pay attention to the argument we pass to its route()
method: "/"
. It's the /
for the web domain URI at http://127.0.0.1/
. It's simply the path, known as "/"
The decorator function @app.route()
will "decorate" the view function that comes after it: the function that generates a message to be send to and rendered by the browser, i.e. the content that the user sees in their browser when they visit 127.0.0.1/
.
We refer to this message-making function as the view function for the route that preceded it, e.g. /
Does it have to be HTML? Nope. Go for the classic message:
@app.route("/")
def homepage():
return "Hello world!"
One thing to note: it doesn't matter what you name the view function (though obviously, name it something that makes sense to you on subsequent re-readings):
@app.route("/")
def booyahshaka():
return "Hello world!"
@app.route("/")
def homepage():
return "Hello world!"
This is what the entirety of app.py should look like:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def whatever_homepage_blah():
return "Hello world!"
if __name__ == '__main__':
app.run()
Switch to your command-line and restart the server with:
python app.py
Then revisit http://127.0.0.1:5000/
Joy:
If you check the Terminal window for wherever you ran python app.py
(i.e. the console window), each visit/reload of your webserver should trigger a new set of server messages:
A quick note about terminology and ports: The 5000
refers to the port number on your computer, not a part of the webserver path. By default, a Flask app will run a web server on your computer at port 5000 for testing. Why 5000
? Because it's not usually taken up by other apps currently running on your system…
Our Flask app is very basic, but it's never too early to learn some good habits for app development, one line at a time.
First, let's change the message that the view function returns for the "/"
route. Don't shut down your app; just switch over to your editor and make a change to app.py. Make it return a multi-line string (denoted with triple quotation marks, """
) just for kicks.
And by now, you should be comfortable with the fact that the name of the view function can be completely arbitrary:
@app.route("/")
def supercalifragalistic():
return """Hello
and
goodbye world!
??? """
A segue: note that the view function must return a value. Your print()
function has no power here, so don't do this:
@app.route("/")
def supercalifragalistic():
print("""Hello
and
goodbye world!
??? """)
If you reload your browser at 127.0.0.1:5000/
, you should see that nothing has changed. By default, a currently-running Flask app won't render changes made to its source code. You have to shut down the app's web server down with Ctrl-C, then restart it with python app.py
.
And then reload the browser.
As with all things inconvenient, the Flask library maintainers have come up with a way to streamline the reloading process. has come up with a way to streamline reloading for development purposes.
In the call to the app.run()
method, pass in the keyword argument, use_reloader=True
:
if __name__ == '__main__':
app.run(use_reloader=True)
Shut your app's web server down and restart it. Any changes you make to app.py should be reflected upon reloading the browser.
Web apps are fun. But with their browser-friendly interface comes the price of a new layer of things to debug. When your app does something weird, is it the browser's fault? Is it in the HTML/JS/CSS code that you hacked together? Or is it a Python error?
Hint: early on, most of your errors are going to be good-ol fashioned Python errors. With your app and web server still running, make an intentional error. For example, alter your view function to return something that is obviously an error, e.g.
"Hello" + 1
In context, with the rest of app.py:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def homepage():
return "Hello" + 1
if __name__ == '__main__':
app.run(use_reloader=True)
When reloading the page, you should get this completely opaque error message in the browser:
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
That vagueness of error message is actually a good thing. When your app is live and facing the entire world and unexpectedly breaks, you don't want everyone in the world to know exactly which line in which file threw what type of error exactly.
But such obfuscation is fatal during development – you don't want to be spending hours guessing where you made a typo. You need the typical error reporting that Python provides, especially as you stumble your way in this new development framework.
Having the Flask app display a proper error message in the web browser is as easy as supplying a keyword argument to the app's run() function:
debug=true
In context:
if __name__ == '__main__':
app.run(debug=True, use_reloader=True)
Ah…sweet, verbose stack trace:
Note that the relevant message is at the top:
TypeError: Can't convert 'int' object to str implicitly
You can read more about Flask's debug mode in its documentation.
print()
to print messages to the consoleWhile print()
functions won't ever impact what's rendered by the browser, they still will print out to the console screen – i.e. the Terminal window in which you ran python app.py
.
There's obviously better, more sophisticated ways to do error logging built into the Flask library…but in a jiffy, go with what works: use print()
calls to tell you what's going on in your app, as you would in any Python script:
from flask import Flask
app = Flask(__name__)
print("initiated the app! With a name of:", __name__)
@app.route('/')
def homepage():
print("Hey I'm visiting the homepage!")
return "Hello world!"
if __name__ == '__main__':
print("App is starting...I think?")
app.run(use_reloader=True)
print("Umm...no idea what/how this message will be seen...")
Here's what my server console looks like when I start the app, visit the homepage, and then shut the app down with Ctrl-C:
Beyond Python syntax and logic errors, there are going to be a myriad of errors exclusively in the domain of running a web application. I'll cover a few of the show-stoppers that you are likely to run into, even with just a "Hello world" app:
In Chrome, this error looks like this:
This could mean either of these things:
localhost:5999
instead of localhost:5000
python app.py
So you're switching between multiple browser and Terminal windows like a pro, but as soon as you try to execute python app.py
, you get this error:
OSError: [Errno 48] Address already in use
This error means that in one of your other Terminal windows, you've already run a web app (Flask, or even something else) that is hogging up port 5000. You need to shut it down and free up the port 5000 if you want to run python app.py
.
Don't forget that classic "Not Found" error. Just because we fixed one instance earlier in the lesson doesn't mean that the web server knows how to deal with all the typos you might throw its way. Instead of visiting a path that you've written a route function for – i.e. /
– try visiting a made-up path, e.g.
http://127.0.0.1:5000/hey/heyhey
Because that path was never defined in app.py, you'll get the 404:
Basically, not only do you have to worry about typos in your Python code, but typos in your browser bar, too.
And that's a Flask app. While we haven't done much of what normally constitutes web application development, it's important to get a feel for the bare foundation of a Flask app and what we get with a few lines of boilerplate code.
Here's all the code for the app we created, though your function/variable names may vary. See how much of it you can reproduce from memory:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def homepage():
return """
Hello world!
This is my first web app!
"""
if __name__ == '__main__':
app.run(use_reloader=True)
In the next lesson, we'll create an app that serves up a "proper" webpage with actual HTML. But it's important to understand that the app's view function really just returns a string object – and thus, a "proper" webpage with "real" HTML is just a big string.