If no one has told you before, Flask is pretty awesome. I recently got into web development using python, and it is already my framework of choice. Its simplicity is the major reason I’m so attracted to it.

This tutorial aims to introduce its basic concepts, and at the end of the tutorial you should have learned how to:

  1. Define routes
  2. Use templates and static files (CSS, JavaScript, etc.)
  3. Handle forms and user requests
  4. Make use of Flask’s templating engine (Jinja 2)

Requirements:

  1. Knowledge of Python basics (declaring variables, defining functions, handling exceptions, writing loops)
  2. Python 2.6 or newer

Okayy, So What is Flask?

From the docs:

Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions.

A framework is a collection of code that makes prototyping, or building stuff easier or faster. So Flask is a very light collection of code that makes building web projects with Python easier.

Installing Virtualenv and Flask

We’ll be using virtualenv for development to create an isolated python environment. So if you don’t already have it, you should install it with this command on terminal:

$ sudo pip install virtualenv

Then let’s create the folder for our app, flask-app, change our directory to the folder, and create our python environment inside it:

$ mkdir flask-app && cd flask-app && virtualenv venv

You should see a message like:

New python executable in venv/bin/python
Installing setuptools, pip............done.

To activate this environment whenever you need to. On OS X and Linux:

$ . venv/bin/activate

On Windows:

$ venv\Scripts\activate

And to exit the virtual environment:

$ deactivate

Next, to activate Flask in our new python environment (make sure your virtualenv is activated):

$ pip install flask

Nice! So we have successfully installed Flask.

Creating our first Flask app

Let’s get our hands dirty by building a simple “Hello World” app to be sure everything is working properly.

We’ll create an app.py file in our flask-app folder which will contain the code for our app.

# app.py

from flask import Flask

app = Flask(__name__)

@app.route('/hello/')
def hello_world():
    return "Hello World, What's up?"

app.run(debug=True)

What we’re doing in the above code is this:

  • First, we import the Flask class (with an uppercase F) from the flask module (with a lowercase f)
  • Then, we create an instance of our app in the __name__ namespace
  • Next, we specify a route to /hello by using the route decorator. A decorator is simply a function that wraps another function. You can read here to learn more about them.
  • Now that a route has been specified, we define a function hello_world() to be called whenever the /hello route is visited. This is the function that the route decorator wraps.
  • Then we run our app and pass in an optional debug parameter. The debug parameter makes it that we don’t need to rerun the app anytime changes are made to the code. It auto-reruns. Really cool.

To run the application:

$ python app.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!

We can visit http://127.0.0.1:5000/hello to see our “Hello World” greeting.

You can also route the homepage to the same hello_world() function by adding another route decorator like this:

@app.route('/') # Homepage route decorator
@app.route('/hello/') # /hello route decorator
def hello_world(): 
    return "Hello World, What's up?"

Decorators are pretty sweet, huh?

Building our Quotes App.

Next, we will build a very simple web app that allows you view quotes from people you search for, and allows you limit the number of quotes you get.

Quotes SPA - forbeginners flask tutorial
What our final result will look like.

File and Folder Structure

Let’s adjust the folder structure of the app to look like this:

flask-app
..../venv
..../static
........app.css
..../templates
........index.html
....app.py
....quotes.py

Flask, by default checks the templates folder for template (html) files, and the static folder for static files (CSS, JavaScript).

We can fill app.css, index.html and quotes.py with some content. We’ll be using foundation, a css framework to make things look a little bit nicer.

// app.css

body {
 background-color: #f6f6f6
}
.main-content {
 max-width: 1100px;
 margin: 40px auto;
}
.main-content h1 {
 font-weight: bolder;
 text-transform: uppercase;
}
.quotes-form {
 margin: 50px 0
}
.quotes-form button[type=submit] {
 width: 100%
}
.quotes-container blockquote {
 color: #4a4a4a;
 font-weight: bold;
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>READ DA QUOTES</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/foundation/6.2.4/foundation.min.css">
<link rel="stylesheet" type="text/css" href="/static/app.css">
</head>
<body>
  <section class="main-content">
    <div class="row hero">
      <div class="columns small-12 medium-12">
         <h1>Welcome to Da Quotes</h1>
         <h2>Search for a person and we'll show ye his finest quotes</h2>
         <blockquote>
         "You never know the value of a quote, till you try to make one up"
         <cite>Flask Developer</cite>
         </blockquote>
      </div>
    </div>

    <div class="row">
      <div class="columns small-12 medium-12 large-8">
        <form class="quotes-form" action="">
           <div class="row">
             <div class="columns large-6">
               <label>
                 Person
                 <input type="text" name="person" value="">
               </label>
             </div>
             <!-- /col6 -->

             <div class="columns large-6">
               <label>
                 Number of Quotes
                 <select name="limit">
                   <option value="all">All</option>
                   <option>1</option>
                   <option>2</option>
                   <option>3</option>
                 </select>
               </label>
             </div>
             <!-- /col6 -->

           </div>
           <!-- /row -->

           <div class="row">
             <div class="columns large-12">
               <button type="submit" class="button expand">Serve me sum quotes!</button>
             </div>
           </div>
         </form>
         <!-- /quotes-form -->

       </div>
       <!-- /large8 -->

     </div>
     <!-- /row -->

   </section>
 

</body>
</html>

The quotes.py file which has the quotes data we would be using, can downloaded from here and saved to the app folder.

Working with template and static files

Now, let’s create our app and serve index.html whenever the homepage is visited

# app.py
from flask import Flask, render_template
app = Flask(__name__)

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

app.run(debug=True)

In the code above, we imported the render_template() method, which allows us render html pages from the templates folder to users whenever they visit a specified route. We then used this method to render the index.html template on the / route.

Working with forms and processing user requests

In index.html let’s specify the route we want the form to submit to, by changing its action attribute.

<form method="GET" class="quotes-form" action="{{ url_for('index') }}">

The weird thing in the double braces is a python expression. Flask’s default templating engine, Jinja 2, lets us do this. Expressions in double curly braces in Jinja 2 are printed to the page.

The url_for() in the braces is a helpful flask function that generates the url for a named function, when the name of the function is passed as a parameter. In the case above, the output on the html page will be /, as that was the route defined for the index() function.

Next, we are going to retrieve the parameters sent when a user submits the form, and use them to get the quotes the user wants.

Lets make some changes to app.py:

from flask import Flask, render_template, request
from quotes import PERSONS

Then:

def getQuotes(person, limit):
  try:
    person = person.lower().replace(' ', '_')
    quotes = PERSONS.get(person)
    if limit and limit != '0':
      try:
        limit = int(limit)
        quotes = quotes[:limit]
      except ValueError:
        quotes = quotes
  except IndexError:
    quotes = []
  return quotes


@app.route('/', methods = ['GET'])
def index():
  person = request.args.get('person', '')
  limit = request.args.get('limit', '')
  quotes = getQuotes(person, limit)
  context = { 'person': person, 'quotes': quotes, 'limit': limit }
  return render_template('index.html', **context)

First, we import the request object from flask, and the PERSONS dictionary from the quotes module.

Next, we use the request global object to access variables from the url, which is appended by the form, when it is submitted. You can also use request.args.items() to see all parameters submitted via the URL. request.form can be used to access POST parameters from a form.

We then define a function getQuotes() to get quotes from our PERSONS dictionary, and limit it to the number specified by the user.

Finally, we create a dict of the variables, context, which we want to pass to our view and unpack it when returning index.html. The last line could also have been written as:

return render_template('index.html', person=person, quotes=quotes, limit=limit)

Showing results in the template file

{% if person %} 
  <h2>Quotes by "{{ person.title() }}"</h2>

  {% if quotes %} 
    <div class="quotes-container">
      {% for quote in quotes %}
        <hr>
        <blockquote>
          {{ quote }} 
        </blockquote>
      {% endfor %}
    </div>
  {% else %}
    <h5>
      Unfortunately we have no quotes by this person currently. 😔
      <br>
      Try searching for: <code>drake</code>, <code>trump</code>, <code>alicia keys</code>, <code>abel tesfaye</code>, <code>buhari</code> instead.
    </h5>
   {% endif %} 
{% endif %}

Here, we basically use the powers of Jinja2 to write some logic in our template and display results when they are present.

Bringing it all together

Our final files should look like this: index.html, app.py.

All the code written can also be found on github, here. Feel free to check it out and ask questions.

Conclusion.

This does not even begin to scratch the surface on what can be done with Flask. I hope to write more in the future about interesting things I build with it. If you have any questions or comments, please don’t be a stranger.