All notes
Flask

Intro

Flask is a minimalist but extremely functional - and powerful - framework. Its author is Armin Ronacher.

pocoo.org. Flaskr is a blogging application by the same team. It is provided as an example. For codes, go to this github repo.

Flask depends on two external libraries: the Jinja2 template engine and the Werkzeug WSGI toolkit.

Flask protects you against one of the most common security problems of modern web applications: cross-site scripting (XSS).

Thread-locals

Flask uses thread-local objects internally so that you don't have to pass objects around from function to function within a request in order to stay threadsafe.

Quick Start


from flask import Flask
app = Flask(__name__)

# For alternative way, see app.run() below.
# app.debug = True

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

if __name__ == '__main__':
    app.run() # Run on http://127.0.0.1:5000/
    # app.run(host='0.0.0.0') # Run in public.
    # app.run(debug=True) # Run in debug mode.

Routing

Variable Rules

# Variable URL Rules:
@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % username

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

Unique URLs

# Similar to directory.
# Client visiting /projects will be directed to /projects/.
@app.route('/projects/')
def projects():
    return 'The project page'

# Similar to file.
# Client visiting /about/ will get a 404 error.
@app.route('/about')
def about():
    return 'The about page'

URL Building

from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
  def index(): pass

@app.route('/login')
  def login(): pass

@app.route('/user/<username>')
  def profile(username): pass

# test_request_context() tells Flask to behave as though it is handling a request. See Context Locals.
with app.test_request_context():
  print url_for('index')
  print url_for('login')
  print url_for('login', next='/')
  print url_for('profile', username='John Doe')

# /
# /login
# /login?next=/
# /user/John%20Doe

HTTP Methods

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        do_the_login()
    else:
        show_the_login_form()

Static Files

StackOverflow. Make a directory "static" at the root of your package, then you could directly use the '/static/foo.bar' URL path.

# The 'static' endpoint name is special:
url_for('static', filename='style.css')
# The file has to be stored on the filesystem as static/style.css.

Rendering Templates

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

Flask will look for templates in the templates folder. So if your application is a module, this folder is next to that module, if it's a package it’s actually inside your package:

Case 1: a module:

/application.py
/templates
    /hello.html
Case 2: a package:
/application
    /__init__.py
    /templates
        /hello.html

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}

Inside templates you also have access to the request, session and g (g is something in which you can store information for your own needs) objects as well as the get_flashed_messages() function.

Templates are especially useful if inheritance is used. Basically template inheritance makes it possible to keep certain elements on each page (like header, navigation and footer).

Automatic Escaping

If name contains HTML it will be escaped automatically.

The following extensions for templates trigger autoescaping: .html, .htm, .xml, .xhtml. Templates loaded from a string will have autoescaping disabled.

If you can trust a variable and you know that it will be safe HTML (for example because it came from a module that converts wiki markup to HTML) you can mark it as safe by using the Markup class or by using the |safe filter in the template.

from flask import Markup
Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
# Markup(u'<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
Markup.escape('<blink>hacker</blink>')
# Markup(u'&lt;blink&gt;hacker&lt;/blink&gt;')
Markup('<em>Marked up</em> &raquo; HTML').striptags()
# u'Marked up \xbb HTML'

The Application Context

The Request Context

Flask servers react to the data a client sent through the global request object. And context locals make it thread-safe.

Context Locals

These global objects are actually proxies to objects that are local to a specific context.

You can completely ignore the case unless you are doing something like unit testing. You will notice that code which depends on a request object will suddenly break because there is no request object.

The easiest solution for unit testing is to use the test_request_context() context manager.

from flask import request

with app.test_request_context('/hello', method='POST'):
    # now you can do something with the request until the
    # end of the with block, such as basic assertions:
    assert request.path == '/hello'
    assert request.method == 'POST'

The other possibility is passing a whole WSGI environment to the request_context() method:

from flask import request

with app.request_context(environ):
    assert request.method == 'POST'

The Request Object

The example shows request.method, request.form.

from flask import request

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)
What happens if the key does not exist in the form attribute? In that case a special KeyError is raised. You can catch it like a standard KeyError but if you don't do that, a HTTP 400 Bad Request error page is shown instead. So for many situations you don't have to deal with that problem.

request.args:

# To access params in URL: (?key=value)
searchword = request.args.get('key', '')
We recommend accessing URL parameters with get or by catching the KeyError because users might change the URL and presenting them a 400 bad request page in that case is not user friendly.

File Uploads

pocoo.org: uploading files.

Don't forget to set the enctype="multipart/form-data" attribute on your HTML form.

Uploaded files are stored in memory or at a temporary location on the filesystem. You can access those files by looking at the request.files.

import os
from flask import Flask, request, redirect, url_for
from werkzeug import secure_filename

UPLOAD_FOLDER = '/path/to/the/uploads'
# Disables uploading HTML files (that would cause XSS problems)
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # Restrict file size.

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['file']
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return redirect(url_for('uploaded_file',
                                    filename=filename))
    return '''
    <!doctype html>
    <title>Upload new File</title>
    <h1>Upload new File</h1>
    <form action="" method=post enctype=multipart/form-data>
      <p><input type=file name=file>
         <input type=submit value=Upload>
    </form>
    '''

from flask import send_from_directory

@app.route('/uploads/<filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'],
                               filename)

secure_filename('../../../../home/username/.bashrc')
# 'home_username_.bashrc'

Cookies

request.cookies.

Instead use the Sessions in Flask that add some security on top of cookies for you.

from flask import request

@app.route('/')
def index():
    username = request.cookies.get('username')
    # use cookies.get(key) instead of cookies[key] to not get a KeyError if the cookie is missing.

# Storing cookies:
# NOTE: cookies are set on response objects.

from flask import make_response

@app.route('/')
def index():
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return resp

Set a cookie when response obj not exists yet

Sometimes you might want to set a cookie at a point where the response object does not exist yet. pocoo.org: deferredCallbacks.

from flask import request
from flask import g

# Registers a function on a list on the g object
def after_this_request(f):
    if not hasattr(g, 'after_request_callbacks'):
        g.after_request_callbacks = []
    g.after_request_callbacks.append(f)
    return f

# Now you can use the after_this_request decorator to mark a function to be called at the end of the request.
# But we still need to call them.

@app.after_request
def call_after_request_callbacks(response):
    for callback in getattr(g, 'after_request_callbacks', ()):
        callback(response)
    return response

@app.before_request
def detect_user_language():
    language = request.cookies.get('user_lang')
    if language is None:
        language = guess_language_from_request()
        @after_this_request
        def remember_language(response):
            response.set_cookie('user_lang', language)
    g.language = language

Mechanisms

# File: flask/__init__.py
from .globals import current_app, g, request, session, _request_ctx_stack

# File: flask/globals.py
from functools import partial
from werkzeug.local import LocalStack, LocalProxy

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of request context')
    return getattr(top, name)

# context locals
_request_ctx_stack = LocalStack()
request = LocalProxy(partial(_lookup_req_object, 'request')) # Here is the definition.

flask's request is defined in globals.py.

partial(func, 'request') returns a new partial object which when called will behave like func('request').

Why use stack? To support multiple APP:

from flask import Flask, url_for  
from gtwisted.core import reactor  
app1 = Flask("app1")  
app2 = Flask("app2")  
 
@app1.route("/index1")  
def index1():  
    return "app1"  
 
@app1.route("/home")  
def home():  
    return "app1home"  
 
@app2.route("/index2")  
def index2():  
    return "app2"  
      
reactor.listenWSGI(8000, app1)  
reactor.listenWSGI(8001, app2)  
reactor.run()  
# Now you can visit 127.0.0.1:8000/index1, 127.0.0.1:80001/index2, .etc

# For testing
with app1.test_request_context():  
    print url_for('index1')  
    with app2.test_request_context():
        print url_for('index2')
    print url_for('home')
# Print:
# /index1
# /index2
# /home

Redirects and Errors

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/example')
def example():
    return redirect("http://www.example.com", code=302)

@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

Responses

The return value from a view function is automatically converted into a response object for you.

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

# If you want to add sth in the header:

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

from flask import Response
@app.route('/ajax_ddl')
def ajax_ddl():
    xml = 'foo'
    return Response(xml, mimetype='text/xml')

Sessions

session allows you to store information specific to a user from one request to the next. This is implemented on top of cookies, and signs the cookies cryptographically.

In order to use sessions you have to set a secret key.

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)

@app.route('/')
def index():
    if 'username' in session:
        return 'Logged in as %s' % escape(session['username'])
    return 'You are not logged in'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form action="" method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))

# set the secret key.  keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

The escape() mentioned here does escaping for you if you are not using the template engine.

Generate good secret keys

import os
os.urandom(24)

Templates

Testing

Logging

pocoo.org: error handling.


ADMINS = ['[email protected]']
if not app.debug:
    import logging
    from logging.handlers import SMTPHandler
    mail_handler = SMTPHandler('127.0.0.1',
                               '[email protected]',
                               ADMINS, 'YourApplication Failed')
    mail_handler.setLevel(logging.ERROR)
    app.logger.addHandler(mail_handler)

if not app.debug:
    import logging
    from themodule import TheHandlerYouWant
    file_handler = TheHandlerYouWant(...)
    file_handler.setLevel(logging.WARNING)
    app.logger.addHandler(file_handler)
FileHandler - logs messages to a file on the filesystem.
RotatingFileHandler - logs messages to a file on the filesystem and will rotate after a certain number of messages.
NTEventLogHandler - will log to the system event log of a Windows system. If you are deploying on a Windows box, this is what you want to use.
SysLogHandler - sends logs to a UNIX syslog.

Configuration Handling

The config attribute of the Flask object is the place where Flask itself puts certain configuration values and also where extensions can put their configuration values. But this is also where you can have your own configuration.

The config is actually a subclass of a dictionary.

wcfNote: Only values in uppercase are actually stored in the config object later on. So make sure to use uppercase letters for your config keys.


app = Flask(__name__)

# config is actually a dictionary.
app.config['DEBUG'] = True

# Certain configuration values are also forwarded to the Flask object:
# wcfNote: the forwarded var is in lower-case (shown here), while the original dict key is in upper-case (shown previously).
app.debug = True

# To update multiple keys at once:
app.config.update(
    DEBUG=True,
    SECRET_KEY='...'
)

Builtin Configuration Values

Configuration from files

Configuration becomes more useful if you can store it in a separate file, ideally located outside the actual application package. This makes packaging and distributing your application possible, including finally modifying the configuration file afterwards.

Best practices:

A common pattern:


app = Flask(__name__)

# Load the default settings first.
app.config.from_object('yourapplication.default_settings')
# Overwrite with custom settings.
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
Set the env var:
Linux: export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
Windows: set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg

Instance Folders

New in Flask version 0.8.

Flask.root_path is the application’s folder.

Suppose:

/home/me/test/
    hi.py

# hi.py:
from flask import Flask

app = Flask(__name__)
print(app.root_path)
print(app.instance_path)

cd /home/me/test
python hi.py
# Output:
# /home/me/test
# /home/me/test/instance

Flask.instance_path is designed to not be under version control and be deployment specific.


# NOTE: this path must be absolute when provided.
app = Flask(__name__, instance_path='/path/to/instance/folder')

The behavior of relative paths in config files can be flipped between "relative to the application root" (the default) to "relative to instance folder" via the instance_relative_config:


app = Flask(__name__, instance_relative_config=True)
app.config.from_object('yourapplication.default_settings')
app.config.from_pyfile('application.cfg', silent=True)

If the instance_path parameter is not provided the following default locations are used:

# Uninstalled module:
/myapp.py
/instance

# Uninstalled package:
/myapp
    /__init__.py
/instance

# Installed module or package:
$PREFIX/lib/python2.X/site-packages/myapp
$PREFIX/var/myapp-instance

Open the config file from instance:


filename = os.path.join(app.instance_path, 'application.cfg')
with open(filename) as f:
    config = f.read()

# or via open_instance_resource:
with app.open_instance_resource('application.cfg') as f:
    config = f.read()

Signals

Patterns for Flask

Message Flashing

pocoo.org: message flashing.

The flashing system basically makes it possible to record a message at the end of a request and access it next request and only next request. This is usually combined with a layout template that does this.

FAQs

Deploy with uwsgi

pocoo.org: deploying with uwsgi.


uwsgi -s /tmp/uwsgi.sock --module myapp --callable app
# or
uwsgi -s /tmp/uwsgi.sock -w myapp:app

Flask dev server run twice?

The Werkzeug reloader spawns a child process so that it can restart that process each time your code changes. Werkzeug is the library that supplies Flask with the development server when you call app.run().

If you set use_reloader to False you'll see the behaviour go away, but then you also lose the reloading functionality:


app.run(port=4004, debug=config.DEBUG, host='0.0.0.0', use_reloader=False)

Flask-SqlAlchemy