1

For development, I'd like to serve static files from the blueprint's static folder. However, I don't want to load all files via the url prefix of the blueprint. Is there a possibility to configure flask, so it looks in all blueprint's static folders and returns the first file it finds? The reason for this is that when I deploy the application, the build process will pull all the files our of the blueprints and put them into a separate folder (similar to Django's collectstatics).

In other words, I have 2 blueprints: foo and bar:

app
  +--- foo
  |    +--- assets
  |    |     +--- css
  |    |     |
  |    |     +--- js
  |    +
  |    |
  |    +--- __init__.py:  foo_blueprint = Blueprint('foo', __name__, static_folder='assets')
  |
  +--- bar
       +
       |
       +--- __init__.py: bar_blueprint = Blueprint('bar', __name__, static_folder='assets')

I want all js files in the particular js subfolders to be available from the url /static/js/file.js. Is this possible?

orange
  • 7,755
  • 14
  • 75
  • 139

1 Answers1

2

You can't with default flask mechanism.

In your your example you will create 3 rules for static forlder:

Rule "/assets/<path:filename>" for "static" endpoint
Rule "/assets/<path:filename>" for "foo.static" endpoint
Rule "/assets/<path:filename>" for "bar.static" endpoint

When flask will match your URL to rule it take first match and return it. In this case it return static endpoint rule. After will dispatched function for this endpoint, e.g. one folder.

PS. I don't sure that exactly static endpoint, better check Map.update method.

But you always can write own request descriptor, which will look at blue prints folders:

class MyApp(Flask):
    def static_dispatchers(self):
        yield super(MyApp, self).send_static_file
        for blueprint in self.blueprints.values():
            yield blueprint.send_static_file

    def send_static_file(self, filename):
        last_exception = None
        for static_dispatcher in self.static_dispatchers():
            try:
                return static_dispatcher(filename)
            except NotFound as e:
                last_exception = e
        raise last_exception

PS. This example doesn't include the blueprint registration sequence, because it stored in dict.

But if you have two files with same name, then first file will processed. For example if you have next structure:

/static
/static/app.js "console.log('app');"
/foo/static/app.js "console.log('foo app');"
/foo/static/blue.js "console.log('foo blue');"
/foo/static/foo.js "console.log('foo self');"
/bar/static/app.js "console.log('bar app');"
/bar/static/blue.js "console.log('foo blue');"
/bar/static/bar.js "console.log('bar self');"

And include scripts to page:

<script src="{{ url_for('static', filename='app.js') }}"></script>
<script src="{{ url_for('foo.static', filename='app.js') }}"></script>
<script src="{{ url_for('bar.static', filename='app.js') }}"></script>
<script src="{{ url_for('foo.static', filename='blue.js') }}"></script>
<script src="{{ url_for('bar.static', filename='blue.js') }}"></script>
<script src="{{ url_for('foo.static', filename='foo.js') }}"></script>
<script src="{{ url_for('bar.static', filename='bar.js') }}"></script>

You will have next result:

app
app
app
foo blue (or bar blue)
foo blue (or bar blue)
foo self
bar self

For js and css can concatenate files with same path, but can't do this for images.

However I prefer use unique URL prefix for each blueprint, because it simple with url_for.

tbicr
  • 24,790
  • 12
  • 81
  • 106
  • Thanks for your response. Do you serve them out of the blueprint folder in production as well? Do you use anything like `require.js` and `r.js` optimisation to merge your js files? If so, does this work with URL prefixes if you end up having 1 `js` file only? – orange Oct 04 '13 at 11:18
  • I don't have blueprints with depending static files, but you can try collect all static files and put to nginx or CDN for production. I do not use `require.js` now, but I believe that it will work fine with URL prefixes as modules for development. And it easy collect and compile your modules for production with little script. – tbicr Oct 04 '13 at 15:05
  • +1 for a very elegant solution to the problem. Note to those who care - if you need to ensure the same order every time you can sort the blueprints so the lookups will always result in the same file in the case of multiple. – Sean Vieira Oct 05 '13 at 00:23
  • Just to be sure, the order is only an issue for js files with the same names, right? If I did have unique file names, this won't be a problem. – orange Oct 05 '13 at 03:39
  • Yes. If you want to save the sequence as for blueprint registration order, then you can override `register_blueprint` and store this sequence. Or you can try concatenate this files as suggested above. Or raise exception to avoid this cases. – tbicr Oct 05 '13 at 07:34