lagom icon indicating copy to clipboard operation
lagom copied to clipboard

Flask Integration when using app factory

Open ewolz3 opened this issue 3 years ago • 5 comments

Looking at the provided Flask integration, it does not look like this works when using Flask application factories (https://flask.palletsprojects.com/en/1.1.x/patterns/appfactories/). At the time I initialize the FlaskIntegration library, my app has not been created yet so I cannot pass it to the init method of Flask.

More information can be found on the Flask extension development documentation: https://flask.palletsprojects.com/en/1.1.x/extensiondev/

This might be related to https://github.com/meadsteve/lagom/issues/124

ewolz3 avatar Mar 22 '21 15:03 ewolz3

@ewolz3 thanks for the input. I've not often used flask in this way (I don't generally use flask much at all).

If #124 was implemented would this work for you? Any problems you can think of?

meadsteve avatar Mar 22 '21 17:03 meadsteve

(On a hacky note I think the current flask integration will actually work if a blueprint instead of an app is provided though it may raise errors with any static type chcking)

meadsteve avatar Mar 22 '21 18:03 meadsteve

@ewolz3 I've added experimental support for blueprints and tagged release 1.3.0 with this. If that's something you're interested in helping test :+1:

meadsteve avatar Mar 22 '21 18:03 meadsteve

I'm not an expert on Flask internals, but my understanding is that Blueprints and App Factories are two separate concepts that often go hand-in-hand (i.e., you will often use Blueprints if you are using an App Factory) so I'm not sure implementation of #124 is enough. Based on the example from this page (https://flask.palletsprojects.com/en/1.1.x/blueprints/), it looks like you can use Blueprints without an app factory (notice how the app = Flask(__name__) is not done inside a function).

I would be happy to help test though.

To give some background on my use case, I use Flask blueprints to version my API (see the top answer in this Stackoverflow thread: https://stackoverflow.com/questions/28795561/support-multiple-api-versions-in-flask).

Those blueprints are registered inside an application factory function so that in my tests, I can easily test the behavior of the application with different configuration settings. Additionally, each test gets its own pristine version of the application and can't possibly interfere with each other.

My app factory then looks something like this:

cors = CORS()

def create_app(config_cls, testing=False):
    app = Flask(__name__)
    app.config.from_object(config_cls)
    if not testing:
        <..do some set-up stuff here..>

    cors.init_app(app)

    from api.v1 import api as api_v1
    from api.v1_1 import api as api_v1_1
    from api.v2 import api as api_v2

    app.register_blueprint(api_v1, url_prefix='/v1')
    app.register_blueprint(api_v1_1, url_prefix='/v1.1')
    app.register_blueprint(api_v2, url_prefix='/v2')

    return app

The guy who wrote the above SO answer has published several books on O'Reilly for Flask development, and also has a really good blog that talks about Flask Application structure (https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xv-a-better-application-structure).

EDIT: Added the return app that was missing in my app factory function

ewolz3 avatar Mar 22 '21 19:03 ewolz3

Thanks @ewolz3 that's some good input. It's making me lean towards an implementation where the blueprints get late bound to the container. The factory function could do the binding. I think it fits nicely with the intention of blueprints

meadsteve avatar Mar 23 '21 07:03 meadsteve