0

Building a Flask App using the Factory pattern. Most libraries for Flask are compatible with the Factory Pattern using an init_app() method.

Taking the example of Flask-RESTfull here, but it is applicable to most other libraries which support the Factory Pattern

The folder structure and code is as below

flaskr/
├── __init__.py
└── resources
    ├── __init__.py
    └── sample_resource.py
# flaskr/__init__.py

from flask import Flask
from flask_restful import Api

api = Api()


def create_app():
    app = Flask(__name__)

    from flaskr.resources import sample_resource  # Resources need to be imported before calling init_app()
    api.init_app(app)

    return app
# flaskr/resources/sample_resource.py

from flask_restful import Resource
from flaskr import api

class UserResource(Resource):

    def get(self):
        return 'Sample Resource'

api.add_resource(UserResource, "/sample")

To run

$ export FLASK_APP=flaskr
$ flask run

Problem

The problem/issue I face is that the Resources need to in the scope in other words api.add_resource(UserResource, "/sample") needs to be called before api.init_app(). This results in importing all resources before api.init_app(app)

Solution 1

To keep create_app() clean, I added all import statements in flaskr/resources/__init__.py and import the resources package like so

# flaskr/resources/__init__.py

from flaskr.resources import sample_resource
# flaskr/__init__.py

def create_app():
    app = Flask(__name__)

    from flaskr import resources
    api.init_app(app)

This keeps create_app() clean but every time I create a new Resource, I need to add an import in resources/__init__.py.

Solution 2

Referring to How to load all modules in a folder So that the __init__.py file will import all the resources in the package. Even though the accepted answer did not work (I didn't explore why) this answer worked. Hence changing the file to

# flaskr/resources/__init__.py

from importlib import import_module
from pathlib import Path

for f in Path(__file__).parent.glob("*.py"):
    module_name = f.stem
    if (not module_name.startswith("_")) and (module_name not in globals()):
        import_module(f".{module_name}", __package__)
    del f, module_name
del import_module, Path

This solves the entire problem, by adding the above code to all such packages (Models, Schemas) etc. But to me picking up file names ending with .py and importing all of them does not feel elegant. For some reason, if there is a non-resource file in the package (even by mistake) it might cause issues (remember this isn't only for Flask-RESTfull, it applies to all libraries using init_app())


Questions

  1. Is there a better way I can structure my App to avoid changes in two places for the creation and deletion of resources?
  2. Can I import resources within the package in a more elegant method? If so, what would be pointers to keep in mind avoiding issues?
  3. Am I understanding the need for resources/models to be in scope during init_app() correctly?

PS: Another library this applies as is - SQLAlchemy (or Flask-SQLAlchemy) where the models need to be in scope before calling init_app()

The SO Answer to - Flask app doesn't recognize flask_restful resources does not solve the issue as it breaks the Factory Pattern

shoaib30
  • 725
  • 8
  • 19

0 Answers0