Getting Started with Flask

This tutorial describes how to create a simple Flask application and set it up under Apache on OS X. Flask is a microframework written in Python. The source code is based on the Flask tutorial. The source code for this tutorial can be found on Github.

As in the tutorial on Flask website, we will create the simple Flaskr blog.

First, create a folder for the project at some convenient location: e.g. ~/flaskr.

In the project root, create two folders static and templates.

In the root folder, create a file flaskr.py containing the following code:

import sqlite3
from flask import Flask, request, session, g, redirect, url_for, \
    abort, render_template, flash
from contextlib import closing

# configuration
DATABASE = '/tmp/flaskr.db'
DEBUG = True
SECRET_KEY = 'developmentn key'
USERNAME = 'admin'
PASSWORD = 'default'

app = Flask(__name__)
app.config.from_object(__name__)

def connect_db():
    return sqlite3.connect(app.config['DATABASE'])

@app.before_request
def before_request():
    g.db = connect_db()


@app.teardown_request
def teardown_request(exception):
    db = getattr(g, 'db', None)
    if db is not None:
        db.close()

def init_db():
    with closing(connect_db()) as db:
        with app.open_resource('schema.sql', mode='r') as f:
            db.cursor().executescript(f.read())
        db.commit()

@app.route('/')
def show_entries():
    cur = g.db.execute('select title, text from entries order by id desc')
    entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
    return render_template('show_entries.html', entries=entries)


@app.route('/add', methods=['POST'])
def add_entry():
    if not session.get('logged_in'):
        abort(401)
    g.db.execute('insert into entries (title, text) values(?,?)',
                 [request.form['title'], request.form['text']])
    g.db.commit()
    flash('New entry was successfully posted')
    return redirect(url_for('show_entries'))

@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != app.config['USERNAME']:
            error = 'Invalid username'
        elif request.form['password'] != app.config['PASSWORD']:
            error = 'Invalid password'
        else:
            session['logged_in'] = True
            flash('You were logged in')
            return redirect(url_for('show_entries'))
    return render_template('login.html', error=error)

@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    flash('You were logged out')
    return redirect(url_for('show_entries'))

if __name__ == '__main__':

    app.run()

Now some explanation. In connect_db() we return a connection to the database, which is sqlite3 in this case.

before_request() function is a function that gets called before every request. We use it to create a db attribute on g which we can use for db requests.

teardown_request() function gets called at the end of a request. This allows us to close the request.

init_db() allows us initialise the database.

The function show_entries() is used to show blog entries, fetching data first from the database.

add_entry() is used to add new blog posts via POST method. The post gets added to the db as well.

login() is used to log the user in. We hardcode the username and password to that which is in our configuration. We also store a flag logged_in in the session on logging in.

logout() is used to log the user out.

Then create a db schema file schema.sql with the following contents:

drop table if exists entries;
create table entries (
id INTEGER PRIMARY KEY autoincrement,
title text not null,
text text not null
);

Next, create a file layout.html in templates folder with the following content:

<!doctype html>
<title>Flaskr</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}" >
<div class=page>
<h1>Flaskr</h1>
    <div class=metanav>
        {% if not session.logged_in %}
        <a href="{{ url_for('login') }}">log in</a>
        {% else %}
        <a href="{{ url_for('logout') }}">log out</a>
        {% endif %}
    </div>
    {% for message in get_flashed_messages() %}
    <div class=flash>{{ message }}</div>
    {% endfor %}
    {% block body %}{% endblock %}
    </div>

This file contains the overall layout of the app and a body block which will include the body of specific pages.

Next, create login.html in the same folder containing the following:

{% extends "layout.html" %}
{% block body %}
  <h2>Login</h2>
  {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
  <form action="{{ url_for('login') }}" method=post>
    <dl>
      <dt>Username:
      <dd><input type=text name=username>
      <dt>Password:
      <dd><input type=password name=password>
      <dd><input type=submit value=Login>
    </dl>
  </form>
{% endblock %}

This form contains markup for the form. As you can see it extends layout.html.

Also, create a file show_entries.html in the same folder with the following content:

{% extends "layout.html" %}
{% block body %}
    {% if session.logged_in %}
    <form action="{{ url_for('add_entry') }}" method=post class=add-entry>
        <dl>
            <dt>Title:</dt>
            <dd><input type="text" size="30" name="title">
            <dt>Text:
            <dd><textarea name=text rows="5" cols="40"></textarea></dd>
            <dd><input type="submit" value="Share"/></dd>

        </dl>
    </form>
    {% endif %}
    <ul class="entries">
        {% for  entry in entries %}
        <li><h2>{{ entry.title }}</h2>{{ entry.text|safe}}</li>
        {% else %}
        <li><em>Unbelievable. No entries here so far</em></li>
        {% endfor %}
    </ul>
{% endblock %}

Here we show post add form and a list of posts if any.

There, now we have the files. To set up the project, create a virtualenv, e.g. flasktest. As follows:

mkvirtualenv flasktest

Then change to the project root folder and install Flask as follows:

pip install Flask

Then run the project using command:

python flaskr.py

Some lines should appear in the terminal indicating the project has started.

You can now view the blog by visiting http://localhost:5000 in your browser.

To set up the project on production, you can use several means including running it via WSGI behind Apache.

First, install flup, a package that provides an random assortment of WSGI servers, using the command:

pip install flup

Next, create a file flaskr.wcgi with the following content:

#!/usr/bin/python
from flup.server.fcgi import WSGIServer
from flaskr import app as application

if __name__ == '__main__':
    WSGIServer(app).run()

Next, install WSGI. If using OS X, you can do that using the command:

brew install homebrew/apache/mod_wsgi

After installation, you’ll see a line telling you to add a line to your apache config file in order to finish installation.

Then add the line to your httpd.conf file. It should be something like:

LoadModule wsgi_module /usr/local/Cellar/mod_wsgi/4.4.11/libexec/mod_wsgi.so

Then, create an Apache configuration file containing the following content:

<VirtualHost *:80>
ServerName flaskr.localhost
WSGIScriptAlias / /path/to/flaskr/flaskr.wsgi
<Directory "/path/to/flaskr/">
Options +Indexes +FollowSymLinks
AllowOverride all
Require all granted
</Directory>
WSGIDaemonProcess flaskr python-path=/path/to/flaskr:/path/to/flaskrvirtualenv/lib/python2.7/site-packages
WSGIProcessGroup flaskr
</VirtualHost>

Next, restart apache as follows:

sudo apachectl restart

Create an entry in your hosts file if necessary to make flaskr.localhost load for you.

That’s it! You should now be able to access your app at http://flaskr.localhost/

Enjoy!