Learning Resources
The best ways to learn about extensions are to:- Look at how other extensions you use are written
- Discuss with others on Discord Chat or GitHub Discussions
- Share your design ideas early for feedback
Naming
A Flask extension typically hasflask 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.
Naming Conventions
A general Python packaging recommendation is that the install name from the package index and the name used inimport 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-Nameimported asflask_nameflask-name-lowerimported asflask_name_lowerFlask-ComboNameimported asflask_combonameName-Flaskimported asname_flask
The Extension Class and Initialization
All 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 aninit_app method to apply the extension instance to the given application instance.
Important: Don’t Store the App
It is important that the app is not stored on the extension, don’t doself.app = app. The only time the extension should have direct access to an app is during init_app, otherwise it should use 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.
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.
Storing Extension State
TheFlask.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.
Adding Behavior
There are many ways that an extension can add behavior. Any setup methods that are available on theFlask object can be used during an extension’s init_app method.
Request Lifecycle Hooks
A common pattern is to usebefore_request to initialize some data or a connection at the beginning of each request, then teardown_request to clean it up at the end. This can be stored on g.
Lazy Initialization
A more lazy approach is to provide a method that initializes and caches the data or connection. For example, aext.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.
Adding Views with Blueprints
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 aBlueprint, then call register_blueprint during init_app to add the blueprint to the app.
Configuration Techniques
There can be multiple levels and sources of configuration for an extension. You should consider what parts of your extension fall into each one.Configuration Levels
-
Per Application Instance - Through
app.configvalues. 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. -
Per Extension Instance - Through
__init__arguments. This configuration usually affects how the extension is used, such that it wouldn’t make sense to change it per deployment. -
Instance Attributes and Decorators - It might be more ergonomic to assign to
ext.value, or use a@ext.registerdecorator to register a function, after the extension instance has been created. -
Global Class Attributes - Changing a class attribute like
Ext.connection_classcan customize default behavior without making a subclass. This could be combined with per-extension configuration to override defaults. - Subclassing - Making the API of the extension itself something that can be overridden provides a very powerful tool for advanced customization.
Flask object itself uses all of these techniques.
Configuration Best Practices
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.Data During a Request
When writing a Flask application, theg object is used to store information during a request. 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.
Application Context Lifecycle
The data ing 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
teardown_appcontextto 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
teardown_request
Views and Models
Your extension views might want to interact with specific models in your database, or some other extension or data connected to your application.Example: Blog Extension with Models
For example, let’s consider aFlask-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.
Using MethodView
One method could be to use class-based views. During__init__, create the model, then create the views by passing the model to the view class’s as_view method.
Using app.extensions
Another technique could be to use an attribute on the extension, such asself.post_model from above. Add the extension to app.extensions in init_app, then access current_app.extensions["simple_blog"].post_model from views.
Providing Base Classes
You may also want to provide base classes so that users can provide their ownPost 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.
Recommended Extension Guidelines
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.1. Maintenance
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.2. Naming Scheme
The naming scheme isFlask-ExtensionName or ExtensionName-Flask. It must provide exactly one package or module named flask_extension_name.
3. Open Source License
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.4. API Requirements
The extension’s API must have the following characteristics:- It must support multiple applications running in the same Python process. Use
current_appinstead ofself.app, store configuration and state per application instance. - It must be possible to use the factory pattern for creating applications. Use the
ext.init_app()pattern.
5. Installation
From a clone of the repository, an extension with its dependencies must be installable in editable mode withpip install -e ..
6. Testing
It must ship tests that can be invoked with a common tool liketox -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.
7. Documentation
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.8. Dependency Management
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.
9. Python Version Support
Indicate the versions of Python supported usingpython_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.