How can I send variables to Jinja template from a Flask decorator?

Question:

Many routes around my blueprinted flask app will need to send ‘sidebar data’ to jinja.

I’m looking for the most efficient way to do this. Their has to be something better than importing my ‘generate_sidebar_data()’ function into every blueprint, repeatedly saying:

var1, var2, var3 = generate_sidebar_data()

and then sending them with ‘render_template’:

return render_template('template.html',
                           var1=var1,
                           var2=var2,
                           var3=var3
                      )

What I want is a decorator that I can put with the route that will do the same thing the above does (run function and send the vars to jinja) but I don’t know if this is possible. How do you send variables to jinja from inside a decorator function?

@blueprint.route('/')
@include_sidebar_data
def frontpage():

    return render_template('template.html')
Asked By: chrickso

||

Answers:

You could create a decorator function like this:

def include_sidebar_data(fn):
    template_name = fn()
    var1, var2, var3 = generate_sidebar_data()
    def wrapped():
        return render_template(template_name, var1=var2, var2=var2)
    return wrapped

@blueprint.route('/')
@include_sidebar_data
def frontpage():

    return 'template.html'
Answered By: janos

You can use a context processor (http://flask.pocoo.org/docs/api/#flask.Flask.context_processor):

def include_sidebar_data(fn):
    @blueprint.context_processor
    def additional_context():
        # this code work if endpoint equals to view function name
        if request.endpoint != fn.__name__:
            return {} 
        var1, var2, var3 = generate_sidebar_data()
        return {
            'var1': var1,
            'var2': var2,
            'var3': var3,
        }
    return fn


@blueprint.route('/')
@include_sidebar_data
def frontpage():
    return render_template('template.html')

UPD: I like the next example more and it is better if the decorator is used for several view functions:

sidebar_data_views = []


def include_sidebar_data(fn):
    sidebar_data_views.append(fn.__name__)
    return fn


@blueprint.context_processor
def additional_context():
    # this code work if endpoint equals to view function name
    if request.endpoint not in sidebar_data_views:
        return {} 
    var1, var2, var3 = generate_sidebar_data()
    return {
        'var1': var1,
        'var2': var2,
        'var3': var3,
    }


@blueprint.route('/')
@include_sidebar_data
def frontpage():
    return render_template('template.html')
Answered By: tbicr

I’m going to propose something even simpler than using a decorator or template method or anything like that:

def render_sidebar_template(tmpl_name, **kwargs):
    (var1, var2, var3) = generate_sidebar_data()
    return render_template(tmpl_name, var1=var1, var2=var2, var3=var3, **kwargs)

Yup, just a function. That’s all you really need, isn’t it? See this Flask Snippet for inspiration. It’s essentially doing exactly the same sort of thing, in a different context.

Answered By: wheaties