Flask: Static files only work for blueprints registered with url_prefix

Created on 8 Nov 2011  ยท  23Comments  ยท  Source: pallets/flask

The option static_folder for blueprints only works correctly when the blueprint has been registered with a url_prefix, otherwise the application's default static URL takes precedence.

There is nothing wrong in that behavior, but it is not documented. It would be nice to have that fact mentioned at http://flask.pocoo.org/docs/blueprints/#static-files and/or http://flask.pocoo.org/docs/api/#blueprint-objects

blueprints docs

Most helpful comment

I was able to workaround this problem by setting the app's static_folder, e.g:

from flask import Flask
from .api import blueprint as api_blueprint
from .ui import blueprint as ui_blueprint

app = Flask(__name__, static_folder = './ui/static')
app.register_blueprint(ui_blueprint)
app.register_blueprint(api_blueprint, url_prefix = '/api')

Basically the root app is still handling the static files, it just knows to find them in the blueprint's static folder instead of the default.

All 23 comments

It doesn't work correctly with url_prefix either if blueprint serves page with '/' url. So, I have to put such static in common static folder and it's not what I exactly want from project file structure.

I ran into this issue as well and found that using static_url_path resolves the ambiguity. This should be documented better, or perhaps the defaults should change.

I was able to workaround this problem by setting the app's static_folder, e.g:

from flask import Flask
from .api import blueprint as api_blueprint
from .ui import blueprint as ui_blueprint

app = Flask(__name__, static_folder = './ui/static')
app.register_blueprint(ui_blueprint)
app.register_blueprint(api_blueprint, url_prefix = '/api')

Basically the root app is still handling the static files, it just knows to find them in the blueprint's static folder instead of the default.

I had the same unpleasant surprise when testing out my blueprint inside an app.

I can't use the blueprint static folder as the app's static folder like mattsmalley suggests (would like my blueprint to remain sandboxed and untouched). I don't want to mount my blueprint on a url_prefix either.

I don't know if there is a reason for this, but it would be nice for a blueprint registered with no url_prefix to be able to "merge" its static assets with the main app, just like it does with the templates.

This seems like a bug, rather than needing additional documentation @deshipu . Currently, if one of more blueprints does not use a url_prefix, all of their static files must be mashed together in the application static folder which is counterproductive for blueprint isolation.

Adding a dummy url_prefix to get around this issue is not desirable either. Also @mattsmalley's solution only works if there is only one blueprint operating without a url_prefix.

@artnez What did you set the static_url_path to? I've tried '/myblueprint/static' and a variety of similar constructions, but url_for('myblueprint.static', filename='test.js') causes a BuildError. Couldn't find any docs on static_url_path and that static_url_path does not make http://localhost:5000/myblueprint/static/test.js available as expected.

Another option. Add a symbolic link to your app's static folder that links in the blueprint's static folder. So for example:

  static
    js
    img
    [etc...]
    users -> ../users/static
  users
    static
      js
      img
      [etc...]

Then refer to static content in the blueprint as /static/users/(js, img, etc.)

After I posted the symbolic link suggestion, I looked in the source code and figured out what @artnez meant by using "static_url_path".

@dghhubble I tried the same thing you did and it works for me. It appears that static_url_path has the same meaning as it does on the Flask object.

A solution involving static_url_path (hinted at by @artnez, @dghubble, and @davidkhess) could look something like this:

blueprint = Blueprint(
    'BLUEPRINTNAME',
    __name__,
    static_folder='static',
    static_url_path='%s/BLUEPRINTNAME' % app.static_url_path
)

Duplicate of #226? I'd leave this open though, as it contains more information.

This can be solved extending the application. For example:

from flask import Flask
from flask.helpers import send_from_directory

from os import path

class MyCustomApp(Flask):

    def send_static_file(self, filename):
        for blueprint_name, blueprint in self.blueprints.items():
            filepath = path.join(blueprint.static_folder, filename)
            if path.exists(filepath):
                return send_from_directory(blueprint.static_folder, filename)
        return super(MyAdminApp, self).send_static_file(filename)

app = MyCustomApp(__name__)
app.register_bluprint(...)

Finally, I solved this problem by using the para 'static_url_path'... But I still wonder if it's a bug of flask-blueprint or something?

I'm also resolving this issue with static_url_path, but I do find it to be fairly undesirable. I struggled to figure out what was happening here until I used url_prefix and found it resolved once referencing a static folder not at the root URI. Since I'm importing the blueprint as a module, I found that this was the appropriate way to handle the issue:

bp = Blueprint('hello_world', __name__, template_folder='templates',
               static_folder='static', static_url_path='/%s' % __name__)

I have the same issue -- mounting a Blueprint with a url_prefix will mess with the static_folder and static_url_path, I have tried every possible combination & it still never find the static dir

Something very strange is up when using static_url_path, and I'm not using blueprints. When I set:

app = Flask(__name__, static_url_path="", static_folder="../public")

then @app.route("/<path:path>") doesn't match on anything even though all my other routes work. When I set:

app = Flask(__name__, static_folder="../public")

then @app.route("/<path:path>") matches on everything including intercepting all links to resources in index.html, which is annoying. It seems my only hope is to rebuild :tired_face: to default Flask locations (<app>/static) and see if that magically works. I dislike magic. Flask 0.10.1.

Update

Okay, after beating this up for several more hours, and no thanks to the docs or examples scattered about the net, I think I partly understand this now -- and I don't have to move my files :fireworks:.

I had to turn my index.html into a template to see what path Flask would generate with url_for('static', filename='bundle.js'). Turns out it was /public/bundle.js. This was not obvious to me since I have a run.py and app/__init__.py setup, and public is at the same level as app. Thus, I had two options with index.html:

  • Template it
  • Hard code "/public/..." into all the addresses

I chose to hard code it (it's a React app and templating would be overkill... and anti-React). I also found what does and does not work with static_url_path in these cases:

Works:

app = Flask(__name__, static_url_path="/public", static_folder="../public")
app = Flask(__name__, static_folder="../public")

Works (for when I had index.html as a template in public):

app = Flask(__name__, static_url_path="/public", static_folder="../public", template_folder="../public")
app = Flask(__name__, static_folder="../public", template_folder="../public")

Partially works (until you need to catch @app.route("/<path:path>") for react-router page refreshes -- or something else weird):

app = Flask(__name__, static_url_path="", static_folder="../public")

Does not work (of course):

app = Flask(__name__, static_url_path="/oops", static_folder="../public")

So basically, static_url_path seems to be less of a setting and more of an acknowledgement of "Okay, I understand what it is going to get set to if I leave it off when I set static_folder; so, I may I be admitted to the playground now?"

Lesson learned: Stay away from static_url_path="" :exclamation:

That might apply to this issue #521 as well. Anyway, don't know how this may or may not apply to Blueprints but, there it is. Perhaps this will be useful to someone headed down the same path or for writing better examples and docs.

Hey guys, thank you @juanitogan, we have been working on this for days and your post really solved it, thanks!

I manually created the rules instead of registering the blueprint

    react = Blueprint('react', __name__, static_folder='../react/dist/')
    app.add_url_rule('/', endpoint='root',
                     view_func=lambda: react.send_static_file("index.html"))
    app.add_url_rule('/<path:filename>',
                     endpoint='react.static',
                     view_func=react.send_static_file)

It works for me when:

  1. set static_folder='static' when creating the Blueprint while static_url_path isn't specified
  2. use '.static' (note the .) in url_for, e.g. I have {{ url_for('.static', filename='js/a-script.js') }} in base.html

And here's my app structure:

โ”œโ”€โ”€ flask_app
โ”‚ย ย  โ”œโ”€โ”€ app.py
โ”‚ย ย  โ”œโ”€โ”€ blueprints
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ __init__.py
โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ a-blueprint
โ”‚ย ย  โ”‚ย ย      โ”œโ”€โ”€ blueprint.py
โ”‚ย ย  โ”‚ย ย      โ”œโ”€โ”€ __init__.py
โ”‚ย ย  โ”‚ย ย      โ”œโ”€โ”€ static
โ”‚ย ย  โ”‚ย ย      โ”‚ย ย  โ””โ”€โ”€ js
โ”‚ย ย  โ”‚ย ย      โ”‚ย ย      โ””โ”€โ”€ a-script.js
โ”‚ย ย  โ”‚ย ย      โ”œโ”€โ”€ templates
โ”‚ย ย  โ”‚ย ย      โ”‚ย ย  โ””โ”€โ”€ base.html
โ”‚ย ย  โ”‚ย ย      โ””โ”€โ”€ views.py
โ”‚ย ย  โ””โ”€โ”€ __init__.py
โ”œโ”€โ”€ config.py
โ””โ”€โ”€ manage.py

@DusanMadar that isn't what this issue is about: you're not mounting the blueprint at /. If you did, you wouldn't get the blueprint's static folder because the app static route would take precedence.

workaround: app.register_blueprint(mod, url_prefix='/')and then just do @mod.route('test') instead of @mod.route('/test') now localhost:5000/test correctly shows what you defined under 'test' in a submodule, which basically has the same effect as no url_prefix

I also happened to run into this issue and I am now at the point that the static folder merges with the application static folder as needed but since I mounted it at a prefix there are 2 related problems.

Only the registration time prefix works as described above. The one in the Blueprint init is not the same.

The other problem is that the url_for template tag is broken for these static files as they are mounted at /static/whatever but the blueprint prefix is computed and prepended in front of it

And this makes it a functionality issue not a documentation one.

Found easy solution. Do it only for your page with url_prefix='/', because other pages work correctly. Can be applied to any static file

main_page/view.py

from flask import render_template, Blueprint

main_page_blueprint = Blueprint(
    'main_page',
    __name__,
    template_folder='templates',
    static_folder='static',
    static_url_path='/project.main_page.static'
)

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

main_page/templates/main_page/index.html

{% block content%}
    <img src="{{url_for('main_page.static', filename='img/photo.png')}}" alt="">
{% endblock %}`

in __init__.py

from project.main_page.view import main_page_blueprint
app.register_blueprint(main_page_blueprint, url_prefix='/')

Hope it works!

Hi, mine worked with this set up:

Folders structures:

|---modules
|     |-----user
|     |-----user_entry
|.              |------static
|.              |------templates
|.              |------__init__.py
|.              |-------my_blueprint.py
|
|---static
|---templates
|---app.py

In my_blueprint file:

my_blueprint = Blueprint("loin_register_module",
                                  __name__,
                                  template_folder="templates",
                                  static_folder="static",
                                  static_url_path="/modules/user_entry/static") #have to show full path from root directory

In my templates files:

<link href="{{url_for('.static', filename='login.css')}}" rel="stylesheet">
<script src="{{url_for('.static', filename='login_page.js')}}"></script>

Hope this will help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jab picture jab  ยท  4Comments

rochacbruno picture rochacbruno  ยท  3Comments

greyli picture greyli  ยท  3Comments

xliiv picture xliiv  ยท  3Comments

tomjaguarpaw picture tomjaguarpaw  ยท  3Comments