Modular Applications with Blueprints

Changelog

New in version 0.7.

Flask uses a concept of blueprints for making application components andsupporting common patterns within an application or across applications.Blueprints can greatly simplify how large applications work and provide acentral means for Flask extensions to register operations on applications.A Blueprint object works similarly to a Flaskapplication object, but it is not actually an application. Rather it is ablueprint of how to construct or extend an application.

Why Blueprints?

Blueprints in Flask are intended for these cases:

  • Factor an application into a set of blueprints. This is ideal forlarger applications; a project could instantiate an application object,initialize several extensions, and register a collection of blueprints.

  • Register a blueprint on an application at a URL prefix and/or subdomain.Parameters in the URL prefix/subdomain become common view arguments(with defaults) across all view functions in the blueprint.

  • Register a blueprint multiple times on an application with different URLrules.

  • Provide template filters, static files, templates, and other utilitiesthrough blueprints. A blueprint does not have to implement applicationsor view functions.

  • Register a blueprint on an application for any of these cases wheninitializing a Flask extension.

A blueprint in Flask is not a pluggable app because it is not actually anapplication – it’s a set of operations which can be registered on anapplication, even multiple times. Why not have multiple applicationobjects? You can do that (see Application Dispatching), but your applicationswill have separate configs and will be managed at the WSGI layer.

Blueprints instead provide separation at the Flask level, shareapplication config, and can change an application object as necessary withbeing registered. The downside is that you cannot unregister a blueprintonce an application was created without having to destroy the wholeapplication object.

The Concept of Blueprints

The basic concept of blueprints is that they record operations to executewhen registered on an application. Flask associates view functions withblueprints when dispatching requests and generating URLs from one endpointto another.

My First Blueprint

This is what a very basic blueprint looks like. In this case we want toimplement a blueprint that does simple rendering of static templates:

  1. from flask import Blueprint, render_template, abort
  2. from jinja2 import TemplateNotFound
  3.  
  4. simple_page = Blueprint('simple_page', __name__,
  5. template_folder='templates')
  6.  
  7. @simple_page.route('/', defaults={'page': 'index'})
  8. @simple_page.route('/<page>')
  9. def show(page):
  10. try:
  11. return render_template('pages/%s.html' % page)
  12. except TemplateNotFound:
  13. abort(404)

When you bind a function with the help of the @simple_page.routedecorator, the blueprint will record the intention of registering thefunction show on the application when it’s later registered.Additionally it will prefix the endpoint of the function with thename of the blueprint which was given to the Blueprintconstructor (in this case also simple_page). The blueprint’s namedoes not modify the URL, only the endpoint.

Registering Blueprints

So how do you register that blueprint? Like this:

  1. from flask import Flask
  2. from yourapplication.simple_page import simple_page
  3.  
  4. app = Flask(__name__)
  5. app.register_blueprint(simple_page)

If you check the rules registered on the application, you will findthese:

  1. >>> app.url_map
  2. Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
  3. <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
  4. <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>])

The first one is obviously from the application itself for the staticfiles. The other two are for the show function of the simple_pageblueprint. As you can see, they are also prefixed with the name of theblueprint and separated by a dot (.).

Blueprints however can also be mounted at different locations:

  1. app.register_blueprint(simple_page, url_prefix='/pages')

And sure enough, these are the generated rules:

  1. >>> app.url_map
  2. Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
  3. <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
  4. <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>])

On top of that you can register blueprints multiple times though not everyblueprint might respond properly to that. In fact it depends on how theblueprint is implemented if it can be mounted more than once.

Blueprint Resources

Blueprints can provide resources as well. Sometimes you might want tointroduce a blueprint only for the resources it provides.

Blueprint Resource Folder

Like for regular applications, blueprints are considered to be containedin a folder. While multiple blueprints can originate from the same folder,it does not have to be the case and it’s usually not recommended.

The folder is inferred from the second argument to Blueprint whichis usually name. This argument specifies what logical Pythonmodule or package corresponds to the blueprint. If it points to an actualPython package that package (which is a folder on the filesystem) is theresource folder. If it’s a module, the package the module is contained inwill be the resource folder. You can access theBlueprint.root_path property to see what the resource folder is:

  1. >>> simple_page.root_path
  2. '/Users/username/TestProject/yourapplication'

To quickly open sources from this folder you can use theopen_resource() function:

  1. with simple_page.open_resource('static/style.css') as f:
  2. code = f.read()

Static Files

A blueprint can expose a folder with static files by providing the pathto the folder on the filesystem with the static_folder argument.It is either an absolute path or relative to the blueprint’s location:

  1. admin = Blueprint('admin', __name__, static_folder='static')

By default the rightmost part of the path is where it is exposed on theweb. This can be changed with the static_url_path argument. Because thefolder is called static here it will be available at theurl_prefix of the blueprint + /static. If the blueprinthas the prefix /admin, the static URL will be /admin/static.

The endpoint is named blueprint_name.static. You can generate URLsto it with url_for() like you would with the static folder of theapplication:

  1. url_for('admin.static', filename='style.css')

However, if the blueprint does not have a url_prefix, it is notpossible to access the blueprint’s static folder. This is because theURL would be /static in this case, and the application’s /staticroute takes precedence. Unlike template folders, blueprint staticfolders are not searched if the file does not exist in the applicationstatic folder.

Templates

If you want the blueprint to expose templates you can do that by providingthe template_folder parameter to the Blueprint constructor:

  1. admin = Blueprint('admin', __name__, template_folder='templates')

For static files, the path can be absolute or relative to the blueprintresource folder.

The template folder is added to the search path of templates but with a lowerpriority than the actual application’s template folder. That way you caneasily override templates that a blueprint provides in the actual application.This also means that if you don’t want a blueprint template to be accidentallyoverridden, make sure that no other blueprint or actual application templatehas the same relative path. When multiple blueprints provide the same relativetemplate path the first blueprint registered takes precedence over the others.

So if you have a blueprint in the folder yourapplication/admin and youwant to render the template 'admin/index.html' and you have providedtemplates as a template_folder you will have to create a file likethis: yourapplication/admin/templates/admin/index.html. The reasonfor the extra admin folder is to avoid getting our template overriddenby a template named index.html in the actual application templatefolder.

To further reiterate this: if you have a blueprint named admin and youwant to render a template called index.html which is specific to thisblueprint, the best idea is to lay out your templates like this:

  1. yourpackage/
  2. blueprints/
  3. admin/
  4. templates/
  5. admin/
  6. index.html
  7. __init__.py

And then when you want to render the template, use admin/index.html asthe name to look up the template by. If you encounter problems loadingthe correct templates enable the EXPLAIN_TEMPLATE_LOADING configvariable which will instruct Flask to print out the steps it goes throughto locate templates on every render_template call.

Building URLs

If you want to link from one page to another you can use theurl_for() function just like you normally would do just that youprefix the URL endpoint with the name of the blueprint and a dot (.):

  1. url_for('admin.index')

Additionally if you are in a view function of a blueprint or a renderedtemplate and you want to link to another endpoint of the same blueprint,you can use relative redirects by prefixing the endpoint with a dot only:

  1. url_for('.index')

This will link to admin.index for instance in case the current requestwas dispatched to any other admin blueprint endpoint.

Error Handlers

Blueprints support the errorhandler decorator just like the Flaskapplication object, so it is easy to make Blueprint-specific custom errorpages.

Here is an example for a “404 Page Not Found” exception:

  1. @simple_page.errorhandler(404)def page_not_found(e): return render_template('pages/404.html')

Most errorhandlers will simply work as expected; however, there is a caveatconcerning handlers for 404 and 405 exceptions. These errorhandlers are onlyinvoked from an appropriate raise statement or a call to abort in anotherof the blueprint’s view functions; they are not invoked by, e.g., an invalid URLaccess. This is because the blueprint does not “own” a certain URL space, sothe application instance has no way of knowing which blueprint error handler itshould run if given an invalid URL. If you would like to execute differenthandling strategies for these errors based on URL prefixes, they may be definedat the application level using the request proxy object:

  1. @app.errorhandler(404)@app.errorhandler(405)def _handle_api_error(ex): if request.path.startswith('/api/'): return jsonify_error(ex) else: return ex

More information on error handling see Custom Error Pages.