This is a project I setup to show the use the application factory pattern in Flask. I set it up because I was running was stuck while refactoring one of my projects to use the factory pattern. The code would run normally, but my test cases where failing with the following two errors:
RuntimeError: application not registered on db instance and no application bound to current context
RuntimeError: working outside of application context
Most Flask examples don’t use the factory pattern so I spent a lot of time searching around to solve the problem. So I thought I would work it out and share it. Hopefully it saves someone else time.
Once your project starts to grow, code organization is everything. Flask provides a number of mechanisms for code organization. One of these mechanism’s is blueprints. Combined with the factory pattern provides a nice way to structure and organise code.
Another problem that the factory pattern helps solve is circular dependencies.
Getting the factory pattern to work isn’t hard. Getting it to work correctly, it turned out, was a little harder. The problem I had was caused in the testing code. In the following section I will briefly explain how to setup and use the factory pattern correctly.
I have added more code than strictly necessary to show the concept of the factory pattern working as a realistic example.
The structure and contents of this example project is:
src │ .gitignore │ readme.md │ manage.py │ requirements.txt ├── instance │ sensitive.cfg ├── test_app_factory │ │ __init__.py │ │ appliction.py │ │ config.py │ │ extensions.py │ │ models.py │ ├── helpers │ │ __init__.py │ │ misc.py │ ├── module │ │ __init__.py │ │ viws.py │ ├── static │ │ favicon-16x16.png │ │ favicon-32x32.png │ └── templates │ index.html └── tests test_basics.py
Okay, what’s important to point out here?
The core of the factory pattern is setup in
extensions.py. All extensions are initialized in
extensions.py. If you add additional extensions make sure to add them to the import statement in
test_app_factory/__init__.py. This is a convent way to shorten import statements.
The actual heavy lifting is done in
application.py. Each part of the application initialization is a separate function, which are called by the main function
app_factory. This function takes a string, which specifies the environment the configuration should be loaded for. The configuration is defined in
The factory pattern in
application.py looks like this:
def app_factory(config, name): app = Flask(...) ... return app
The function calls a number of functions that load the configuration settings, extensions, blueprints, etc.
Using the factory is really easy, just use the following call:
app = app_factory('TST')
To access the app object in modules after the application has been initialized is done using the proxy provided by Flask:
from flask import current_app as app
Now for the part that was driving met crazy, the testing code. I still do not understand fully why it is the only place in my code that was causing a problem, probably has to do with the way
unittest works. Anyway, to get the factory pattern to work you need to add
app_context to specific statements. Here is an example.
class TestCase(unittest.TestCase): @classmethod def setUpClass(cls): cls.app = app_factory('TST') def setUp(self): with self.app.app_context(): self.client = app.test_client() db.create_all() def tearDown(self): with self.app.app_context(): db.session.remove() db.drop_all() def test_add_user(self): with self.app.app_context(): db.session.add(User(name='test user', email='[email protected]')) db.session.commit()
Finding good examples isn’t always easy. The factory pattern can really help to organize the code and make it more readable and maintainable.
Any suggestions how I can further improve the code? I would love to hear from you!
2 thoughts on “Flask application factory pattern and testing”
your getting the error because you are using the real app_request_context object, if you want to do tests you can use the app.test_request_context method to create a context for testing, and for your original db errors, make sure that inside of your `app_factory` function make sure your calling db.init_app(app) so by the time its returned from the app_factory its already bound.
Thanks for the post! I’m still new to flask so tips such as this are really helpful!