Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blueprint-specific assets in Flask using Flask-Assets?

Tags:

python

flask

I'm pretty new to Flask, for what it's worth. I'm using blueprints to break up my code and am trying to use Flask-Assets for serving up asset links. For some reason, though, I can't get Flask-Assets to work within a blueprint.

Code structure:

/modules
    /base
        __init__.py
    __init__.py
/static
    # ... stuff
/templates
    /layout.html

In /modules/base/__init__.py:

from flask import Blueprint, render_template, request
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.assets import Environment, Bundle
from flask import current_app as app

default = Blueprint('base', __name__)

assets = Environment(app)

css = Bundle('css/bootstrap.min.css')
assets.register('css_all', css)

js = Bundle('js/jquery-1.7.2.min.js', 'js/bootstrap.min.js')
assets.register('js_all', js)

@default.route('/')
def index():

    return render_template('index/index.html')

And the base layout has this in it:

{% assets "css_all" %}
    <link rel="stylesheet" href="{{ ASSET_URL }}" />
{% endassets %}
{% assets "js_all" %}
    <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}

Actually what I have in /modules/base/__init__.py right now doesn't even work, as it throws "RuntimeError: working outside of application context". If I move the assets code into the index function it does work, but it seems weird to have to repeat that code every time I want to add assets. Is there a better way to do this? Am I missing something?

Edit: Updated /modules/base/__init__.py per codecool's comment, but now it throws an "TemplateSyntaxError: Encountered unknown tag 'assets'" error. I think this one is because the assets object isn't getting passed through to the app.

Edit 2: So taking some advice from how SQLAlchemy does things, I got this to sort of work with the following:

In my main __init__.py:

from flask import Flask
from modules.base import default, assets as base_assets
from flask.ext.assets import Environment, Bundle

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

base_assets.init_app(app)

In base/__init__.py: from flask import Blueprint, render_template, request from flask.ext.assets import Environment, Bundle from flask import current_app as app

default = Blueprint('base', __name__)

assets = Environment(app)

css = Bundle('css/bootstrap2.min.css')
assets.register('css_all', css)

js = Bundle('js/jquery-1.7.2.min.js', 'js/bootstrap.min.js')
assets.register('js_all', js)

The trick here is creating assets in the blueprint, importing it into the base init, and then calling .init_app there. Where this breaks down, however, is in having two blueprints that do this. The last one called always wins out. Maybe it'd be possible to pull this out into some common module or something..

like image 908
greggilbert Avatar asked Jul 09 '12 04:07

greggilbert


People also ask

How do blueprints work in flask?

Each Flask Blueprint is an object that works very similarly to a Flask application. They both can have resources, such as static files, templates, and views that are associated with routes. However, a Flask Blueprint is not actually an application. It needs to be registered in an application before you can run it.

Why blueprints are used in flask?

Flask uses a concept of blueprints for making application components and supporting common patterns within an application or across applications. Blueprints can greatly simplify how large applications work and provide a central means for Flask extensions to register operations on applications.

What are flask assets?

Flask-Assets is an extension for managing your static files. There are two really useful tools that Flask-Assets provides. First, it lets you define bundles of assets in your Python code that can be inserted together in your template. Second, it lets you pre-process those files.


2 Answers

You dont need to call _get_current_object method on current_app and thats why you are getting the error. current_app is actually a proxy for the app. Code should be like this:

    app = current_app
    assets = Environment(app)

Infact, you don't need to assign it as variable. Do this:

     from flask import current_app as app

     assets = Environment(app)

You are getting RuntimeError because _get_current_object is available only when a request is going on otherwise you have to use current_app. When you moved assets initializing code inside the index view then it started working as it was now inside a request.

_get_current_object's usage comes in Signals functionality in Flask and you can read about it here http://flask.pocoo.org/docs/signals/#sending-signals.

Edit:

This is happening because this is not the right place to initialize Flask assets. It has to be initialized in modules/__init__.py where jinja environment is set up when app is created. Remember blueprints code is run only when it is accessed. All the global initializations should be done in the module where your app is defined i.e where this code is present:

    app = Flask(__name__)
    assets = Environment(app)
like image 71
codecool Avatar answered Sep 19 '22 12:09

codecool


The trick here is creating assets in the blueprint, importing it into the base init, and then calling .init_app there. Where this breaks down, however, is in having two blueprints that do this. The last one called always wins out. Maybe it'd be possible to pull this out into some common module or something..

Take a look at this article. It explains very well how to make assets blueprint-specific. https://hackersandslackers.com/flask-assets/#blueprint-specific-bundles

You can declare bundles wherever you want. In the article, they are declared in a separate file assets.py. Alternatively you can set up your blueprint-specific bundles where you declare the blueprints themself. Then import those to your main project __init__.py file, and register them after you register the blueprint.

I've modified your example here:

/__init__.py:

from flask import Flask
from flask-assets import Environment
from modules.base import bp as base_bp, assets as base_assets
# Assuming more blueprints in addition to modules.base...
from modules.home import bp as home_bp, assets as home_assets
from modules.auth import bp as auth_bp, assets as auth_assets

# Declare Flask App
app = Flask(__name__)

# Register Blueprints
app.register_blueprint(base_bp)
app.register_blueprint(home_bp)
app.register_blueprint(auth_bp)

# Register Assets
bundles = {**base_assets, **home_assets, **auth_assets}  # Combines all bundle-dictionaries into one
assets = Environment(app)
assets.register(bundles)

/modules/base/__init__.py:

from flask import Blueprint, render_template
from flask-assets import Bundle

# Declare Blueprint
bp = Blueprint("base", __name__)

# Declare Blueprint-Specific Assets
bundles = {
    "base_css": Bundle(
        "base/css/bootstrap.min.css"
    ),
    "base_js": Bundle(
        "base/js/jquery-1.7.2.min.js",
        "base/js/bootstrap.min.js"
    )
}

@bp.route('/')
def index():
    return render_template('index/index.html')

/modules/base/templates/layout.html:

{% assets "base_css" %}
    <link rel="stylesheet" href="{{ ASSET_URL }}" />
{% endassets %}
{% assets "base_js" %}
    <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
like image 31
Irrer Polterer Avatar answered Sep 22 '22 12:09

Irrer Polterer