docs/extensiondev.rst
.. currentmodule:: flask
Extensions are extra packages that add functionality to a Flask
application. While PyPI_ contains many Flask extensions, you may not
find one that fits your need. If this is the case, you can create your
own, and publish it for others to use as well.
This guide will show how to create a Flask extension, and some of the common patterns and requirements involved. Since extensions can do anything, this guide won't be able to cover every possibility.
The best ways to learn about extensions are to look at how other
extensions you use are written, and discuss with others. Discuss your
design ideas with others on our Discord Chat_ or
GitHub Discussions_.
The best extensions share common patterns, so that anyone familiar with using one extension won't feel completely lost with another. This can only work if collaboration happens early.
A Flask extension typically has flask in its name as a prefix or
suffix. If it wraps another library, it should include the library name
as well. This makes it easy to search for extensions, and makes their
purpose clearer.
A general Python packaging recommendation is that the install name from
the package index and the name used in import statements should be
related. The import name is lowercase, with words separated by
underscores (_). The install name is either lower case or title
case, with words separated by dashes (-). If it wraps another
library, prefer using the same case as that library's name.
Here are some example install and import names:
Flask-Name imported as flask_nameflask-name-lower imported as flask_name_lowerFlask-ComboName imported as flask_combonameName-Flask imported as name_flaskAll extensions will need some entry point that initializes the
extension with the application. The most common pattern is to create a
class that represents the extension's configuration and behavior, with
an init_app method to apply the extension instance to the given
application instance.
.. code-block:: python
class HelloExtension:
def __init__(self, app=None):
if app is not None:
self.init_app(app)
def init_app(self, app):
app.before_request(...)
It is important that the app is not stored on the extension, don't do
self.app = app. The only time the extension should have direct
access to an app is during init_app, otherwise it should use
:data:.current_app.
This allows the extension to support the application factory pattern, avoids circular import issues when importing the extension instance elsewhere in a user's code, and makes testing with different configurations easier.
.. code-block:: python
hello = HelloExtension()
def create_app():
app = Flask(__name__)
hello.init_app(app)
return app
Above, the hello extension instance exists independently of the
application. This means that other modules in a user's project can do
from project import hello and use the extension in blueprints before
the app exists.
The :attr:Flask.extensions dict can be used to store a reference to
the extension on the application, or some other state specific to the
application. Be aware that this is a single namespace, so use a name
unique to your extension, such as the extension's name without the
"flask" prefix.
There are many ways that an extension can add behavior. Any setup
methods that are available on the :class:Flask object can be used
during an extension's init_app method.
A common pattern is to use :meth:~Flask.before_request to initialize
some data or a connection at the beginning of each request, then
:meth:~Flask.teardown_request to clean it up at the end. This can be
stored on :data:.g, discussed more below.
A more lazy approach is to provide a method that initializes and caches
the data or connection. For example, a ext.get_db method could
create a database connection the first time it's called, so that a view
that doesn't use the database doesn't create a connection.
Besides doing something before and after every view, your extension
might want to add some specific views as well. In this case, you could
define a :class:Blueprint, then call :meth:~Flask.register_blueprint
during init_app to add the blueprint to the app.
There can be multiple levels and sources of configuration for an extension. You should consider what parts of your extension fall into each one.
app.config
values. This is configuration that could reasonably change for each
deployment of an application. A common example is a URL to an
external resource, such as a database. Configuration keys should
start with the extension's name so that they don't interfere with
other extensions.__init__
arguments. This configuration usually affects how the extension
is used, such that it wouldn't make sense to change it per
deployment.ext.value, or use a @ext.register decorator to register a
function, after the extension instance has been created.Ext.connection_class can customize default
behavior without making a subclass. This could be combined
per-extension configuration to override defaults.The :class:~flask.Flask object itself uses all of these techniques.
It's up to you to decide what configuration is appropriate for your extension, based on what you need and what you want to support.
Configuration should not be changed after the application setup phase is complete and the server begins handling requests. Configuration is global, any changes to it are not guaranteed to be visible to other workers.
When writing a Flask application, the :data:~flask.g object is used to
store information during a request. For example the
:doc:tutorial <tutorial/database> stores a connection to a SQLite
database as g.db. Extensions can also use this, with some care.
Since g is a single global namespace, extensions must use unique
names that won't collide with user data. For example, use the extension
name as a prefix, or as a namespace.
.. code-block:: python
# an internal prefix with the extension name
g._hello_user_id = 2
# or an internal prefix as a namespace
from types import SimpleNamespace
g._hello = SimpleNamespace()
g._hello.user_id = 2
The data in g lasts for an application context. An application context is
active during a request, CLI command, or with app.app_context() block. If
you're storing something that should be closed, use
:meth:~flask.Flask.teardown_appcontext to ensure that it gets closed when the
app context ends. If it should only be valid during a request, or would not be
used in the CLI outside a request, use :meth:~flask.Flask.teardown_request.
Your extension views might want to interact with specific models in your
database, or some other extension or data connected to your application.
For example, let's consider a Flask-SimpleBlog extension that works
with Flask-SQLAlchemy to provide a Post model and views to write
and read posts.
The Post model needs to subclass the Flask-SQLAlchemy db.Model
object, but that's only available once you've created an instance of
that extension, not when your extension is defining its views. So how
can the view code, defined before the model exists, access the model?
One method could be to use :doc:views. During __init__, create
the model, then create the views by passing the model to the view
class's :meth:~views.View.as_view method.
.. code-block:: python
class PostAPI(MethodView):
def __init__(self, model):
self.model = model
def get(self, id):
post = self.model.query.get(id)
return jsonify(post.to_json())
class BlogExtension:
def __init__(self, db):
class Post(db.Model):
id = db.Column(primary_key=True)
title = db.Column(db.String, nullable=False)
self.post_model = Post
def init_app(self, app):
api_view = PostAPI.as_view(model=self.post_model)
db = SQLAlchemy()
blog = BlogExtension(db)
db.init_app(app)
blog.init_app(app)
Another technique could be to use an attribute on the extension, such as
self.post_model from above. Add the extension to app.extensions
in init_app, then access
current_app.extensions["simple_blog"].post_model from views.
You may also want to provide base classes so that users can provide
their own Post model that conforms to the API your extension
expects. So they could implement class Post(blog.BasePost), then
set it as blog.post_model.
As you can see, this can get a bit complex. Unfortunately, there's no
perfect solution here, only different strategies and tradeoffs depending
on your needs and how much customization you want to offer. Luckily,
this sort of resource dependency is not a common need for most
extensions. Remember, if you need help with design, ask on our
Discord Chat_ or GitHub Discussions_.
Flask previously had the concept of "approved extensions", where the Flask maintainers evaluated the quality, support, and compatibility of the extensions before listing them. While the list became too difficult to maintain over time, the guidelines are still relevant to all extensions maintained and developed today, as they help the Flask ecosystem remain consistent and compatible.
An extension requires a maintainer. In the event an extension author
would like to move beyond the project, the project should find a new
maintainer and transfer access to the repository, documentation,
PyPI, and any other services. The Pallets-Eco_ organization on
GitHub allows for community maintenance with oversight from the
Pallets maintainers.
The naming scheme is Flask-ExtensionName or ExtensionName-Flask.
It must provide exactly one package or module named
flask_extension_name.
The extension must use an open source license. The Python web ecosystem tends to prefer BSD or MIT. It must be open source and publicly available.
The extension's API must have the following characteristics:
current_app instead of self.app, store
configuration and state per application instance.ext.init_app() pattern.From a clone of the repository, an extension with its dependencies
must be installable in editable mode with pip install -e ..
It must ship tests that can be invoked with a common tool like
tox -e py, nox -s test or pytest. If not using tox,
the test dependencies should be specified in a requirements file.
The tests must be part of the sdist distribution.
A link to the documentation or project website must be in the PyPI
metadata or the readme. The documentation should use the Flask theme
from the Official Pallets Themes_.
The extension's dependencies should not use upper bounds or assume
any particular version scheme, but should use lower bounds to
indicate minimum compatibility support. For example,
sqlalchemy>=1.4.
Indicate the versions of Python supported using python_requires=">=version".
Flask and Pallets policy is to support all Python versions that are not
within six months of end of life (EOL). See Python's EOL calendar_ for
timing.
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask .. _Discord Chat: https://discord.gg/pallets .. _GitHub Discussions: https://github.com/pallets/flask/discussions .. _Official Pallets Themes: https://pypi.org/project/Pallets-Sphinx-Themes/ .. _Pallets-Eco: https://github.com/pallets-eco .. _EOL calendar: https://devguide.python.org/versions/