Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static files only work for blueprints registered with url_prefix #348

Closed
deshipu opened this issue Nov 8, 2011 · 23 comments · Fixed by #2686
Closed

Static files only work for blueprints registered with url_prefix #348

deshipu opened this issue Nov 8, 2011 · 23 comments · Fixed by #2686

Comments

@deshipu
Copy link

deshipu commented Nov 8, 2011

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

@semirook
Copy link

semirook commented Jan 4, 2012

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.

@artnez
Copy link

artnez commented Feb 12, 2012

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.

@mattsmalley
Copy link

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.

@nicolashery
Copy link

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.

@dghubble
Copy link

dghubble commented Jan 7, 2013

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.

@davidkhess
Copy link

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

app
  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.)

@davidkhess
Copy link

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.

@wbolster
Copy link

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
)

@untitaker
Copy link
Contributor

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

@tzulberti
Copy link

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(...)

@whypro
Copy link

whypro commented Jul 5, 2013

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?

@angstwad
Copy link

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__)

@maelp
Copy link

maelp commented Sep 25, 2014

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

shimizukawa pushed a commit to shimizukawa/sphinx-websupport-app that referenced this issue Apr 26, 2015
Move templates and static assets to corresponding blueprint package,
fixup a few references (url_for calls).

Leave openid at root (instead of moving to sphinxweb.auth) because
pallets/flask#348 (can't mount blueprint
static files to /static) & it is referred to from the root CSS.

Maybe look into e.g. flask-assets to see if it can handle these cases
correctly and/or CSS concatenation so auth CSS can be in auth
blueprint?

Though can also just load multiple CSS (can blueprint override base to
add CSS? Maybe use blueprint-local base?)
@oxbambooxo
Copy link

https://vilimpoc.org/blog/2012/11/21/serving-static-files-from-root-and-not-static-using-flask/
this essay look like that situation can be resolved

@juanitogan
Copy link

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 😫 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 🎆.

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=""

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.

@danielmhair
Copy link

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

@jbejar
Copy link

jbejar commented Apr 14, 2017

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)

@DusanMadar
Copy link

DusanMadar commented Apr 23, 2017

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
├── 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

@davidism
Copy link
Member

davidism commented Apr 23, 2017

@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.

@davidism davidism added the docs label Apr 26, 2017
@swiknaba
Copy link

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

@kozmaz87
Copy link

kozmaz87 commented Jul 11, 2017

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.

@davidism davidism modified the milestone: 1.0 Jul 31, 2017
Interest1024 added a commit to codesydney/censusplus-flask that referenced this issue Mar 4, 2018
Please refer to the discussion in: pallets/flask#348
I used the method suggested by mattsmalley.

The key modification is adding static_folder = './main/static' in the sentence:
app = Flask(__name__, static_folder = './main/static')
in app\__init__.py

Then change the url "/main/*" to url "/*" in code (for example, loadmap.js, mainmenu).
@savvasenok
Copy link

savvasenok commented Jan 4, 2020

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!

shimizukawa pushed a commit to shimizukawa/sphinx-demo-webapp that referenced this issue Jul 4, 2020
Move templates and static assets to corresponding blueprint package,
fixup a few references (url_for calls).

Leave openid at root (instead of moving to sphinxweb.auth) because
pallets/flask#348 (can't mount blueprint
static files to /static) & it is referred to from the root CSS.

Maybe look into e.g. flask-assets to see if it can handle these cases
correctly and/or CSS concatenation so auth CSS can be in auth
blueprint?

Though can also just load multiple CSS (can blueprint override base to
add CSS? Maybe use blueprint-local base?)
@Es489
Copy link

Es489 commented Sep 15, 2020

Hi, mine worked with this set up:

Folders structures:

flask_app
|---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!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 14, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.