pythonflaskjinja2static-files

How to serve static files from submodules in Flask


I'm writing a Flask application divided into modules, with each module residing in a separate folder that includes 'templates' and 'static' folders.

In each module, I'm passing a global variable 'custom_js' to the template:

module_bp = Blueprint('module', __name__, template_folder='templates', static_folder='static')

# pass custom js files to the base template
@module_bp.context_processor
def custom_js_context():
    return {'custom_js': ['some_file.js']}

I include these JavaScript files in the main template base.html like this:

{% if custom_js %}
    {% for js_file in custom_js %}
        <script src="{{ url_for('static', filename='js/' + js_file) }}"></script>
    {% endfor %}
{% endif %}

The code above results in adding to base.html. The physical file is located at /module/static/js/some_file.js, but accessing http://127.0.0.1:8080/static/js/some_file.js results in a 404 error.

How can I correctly serve these JavaScript files from the modules?


One idea is to modify the blueprint to:

module_bp = Blueprint('module', 
                         __name__, 
                         template_folder='templates', 
                         static_folder='static',
                         static_url_path='/module/static'
                         )

and serving the files to the template like this:

@module_bp.context_processor
def custom_js_context():
    custom_js_files = ['some_file.js']
    custom_js_with_prefix = ['/module/static/js' + filename for filename in custom_js_files]
    return {'custom_js': custom_js_with_prefix}

with updated base.html loop

{% if custom_js %}
    {% for js_file in custom_js %}
        <script src="{{ js_file) }}"></script>
    {% endfor %}
{% endif %}

However, I'm not sure to what extent this is the correct approach.


Solution

  • This is how I solve this (whether that's the ordinary way with Flask, I can't tell):

        _controller_name_ = "bugsandfeatures"
        _url_prefix_ = '/' + _controller_name_
    
        bp = Blueprint("bugsandfeatues", __name__,
                       template_folder='templates',
                       static_folder="static",
                       url_prefix=_url_prefix_)
    

    Note the url_prefix, which makes sure that the blueprint actually serves static files from its own path (see [https://flask.palletsprojects.com/en/3.0.x/blueprints/#static-files][1]).

    Now you can:

        @bp.context_processor
        def custom_js_context():
            return {'custom_js': url_for('.static', filename='my_file.js')}
    

    note that "." in front of static. It makes sure the endpoint is resolved by the current blueprint. And when you pick the variable up in the template, you get ''/bugsandfeatures/static/my_file.js''. [1]: https://flask.palletsprojects.com/en/3.0.x/blueprints/#static-files