Flask Blueprint Initialization - Initializing some global variables

j 34 picture j 34 · Jan 30, 2016 · Viewed 7.1k times · Source

I'm new to Flask and I'm about to write a larger application. So far I'v divided functionality into blueprints. Now I want to be able to set some global variable from within a blueprint during initialization of it (so outside of a request context). Basically, I want to automatically initialize a navigation list for certain blueprints, so each blueprint needs to tell my base application how it wants to be named and what its default route is.

My goal is that other people can extend my application by just putting their custom blueprint into my application's "plugin" folder. In this scenario, my application doesn't know their routes or names. It needs to automatically learn it while loading the specific blueprint...

To explain it in some other way: I have a main application containing some sub applications implemented as blueprints. The main application should hold a navigation bar referencing to all sub applications (blueprints). How can the blueprint register something in that main menu navigation variable on (e.g. on initialization)?

(I didn't find a way to access something like "self.parent" or the application context from a blueprint. Don't blueprints have something like a constructor?)

Answer

miso picture miso · Jan 30, 2016

so each blueprint needs to tell my base application how it want's to be named and what it's default route is.

When you create a blueprint you already pass its name in the first parameter:

simple_page = Blueprint('simple_page')

You can pass to the constructor the url_prefix value too

simple_page = Blueprint('simple_page', url_prefix='/pages')

I have a main application containing some sub applications implemented as blueprints. The main application should hold a navigation bar referencing to all sub applications (blueprints)

This is an example in one python module, you should split each blueprint in its own module.

from flask import Flask, Blueprint, render_template

# ADMIN
admin = Blueprint('admin', __name__, url_prefix='/admin')

@admin.route('/')
def admin_index():
    return 'Admin module'

@admin.route('/settings')
def settings():
    return 'Admin Settings'


# USER
user = Blueprint('user', __name__, url_prefix='/user')

@user.route('/')
def user_index():
    return 'User module'

@user.route('/profile')
def profile():
    return 'User Profile'


app = Flask(__name__)
app.register_blueprint(admin)
app.register_blueprint(user)

@app.route('/')
def index():
    return render_template('index.html', blueprints=app.blueprints)

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True, port=7000)

The Jinja2 template. Remember to place this file in the templates folder in you root project, which is where flask search the templates by default.

<ul>
  {% for bp_name, bp in blueprints.iteritems() %}
    <li><a href="{{ bp.url_prefix }}">{{ bp.name }}</a></li>
  {% endfor %}
</ul>

From the flask main application object, in this case app, you can access the list of blueprints registered; app.blueprints, which is a python dictionary with the name of the blueprint that you passed in the constructor. So you pass this object to you template and loops on it to render the name and URL in the menu.

Furthermore, what if I want to enable blueprint authors to pass more data in a standardized way (e.g. in which color to display the link to his piece, or whatever...)

As this is a blueprint specific data, I think a good solution is to extend the Blueprint class and implement a custom method to save extra data in a standardized way and then access then from the main application object.

custom.py

from flask import Blueprint

class MyBlueprint(Blueprint):

    bp_data = {}

    def set_data(data):
        # here you can make extra task like ensuring if 
        # a minum group of value were provided for instance
        bp_data = data

admin.py

from .custom import MyBlueprint

admin = MyBlueprint('admin', __name__, url_prefix='/admin')
admin.set_data({'color': '#a569bd', 'enabled': true})

# all the blueprint's routes
...

app.py

from admin import admin

app = Flask(__name__)
app.register_blueprint(admin)

@app.route('/')
def index():
    for a, b in app.blueprints:
        print b.bp_data['color']
        print b.bp_data['enabled']
    ...

Of course, my custom Blueprint class needs more work on it, like validating what type of data its being passed or throwing an error if there isn't a required value, like; title, require_auth, etc. From this point, it's you who must define what is the minimum required data that a blueprint must provide to your main application to work properly.