flask does not see change in .js file
Asked Answered
D

5

72

I made a change on one of the .js files that I use and no matter what I do, flask insists on picking up, from memory cache, the last version of the file, without the change.

To clarify, I have the following structure. It all starts with foo.html

return render_template foo.html

foo.html has a form inside that calls flask with some data and then returns a second template bar.html:

return render_template bar.html

This second template calls some .js file, placed in the static folder, but it doesn't update when the code changes.

I mention the structure above because if the .js file was placed on foo.html instead of bar.html then Flask would pick up the new changes on the file. But in bar.html Flask completely ignores them.

What is happening?

The only thing that worked was to click on "disable cache" on the browser and reload again.

Diahann answered 14/12, 2016 at 14:3 Comment(9)
If you're able to make a change in the browser to make a JS file show up, then the problem probably isn't on the server.Compressed
it is not problem with flask but browser keeps old version in cache to work faster. Normally you will work with static .js and it makes no problem. Some servers use dynamic urls for scripts to force browser to load new version - ie. "script.js?some_variable=dynamic_value" . dynamic_value can be version number or date of file.Vanny
but how can I make the browser not use the cached version? Also, why does the second case work fine then?Diahann
A good way to work around this type of browser cache issue is to include version numbers in your static js files... e.g. instead of custom.js use custom.1.2.jsVenge
I'm in the process of development though, I'm constantly changing the .js file, surely one cannot be expected to rename the files with every single change.Diahann
Yes, I just do hard refreshes in dev. On Mac in Chrome this is CMD-RVenge
if Ctrl+F5 or Ctrl+R doesn't work then disable cache in browser till you create final version or use "script.js?some_variable=file_timestamp" and add create function to generat url with parameter.Vanny
so Ctrl+R does not work but cmnd + R does work, on mac at least. @PJSantoro, if you want to post as a solution I'll accept.Diahann
You can also disable cache, this will solve the issue. On chrome, press ctrl+shift+I then go to Network, then at the top, check the box that says "disable cache"Trincomalee
V
105

Ultimately this is a frustrating browser cache issue, which can be solved by forcing the browser to do a "hard refresh", which is going to be a browser/OS dependent keystroke, but generally this works:

  • Windows: Ctrl+F5
  • Mac: Cmd+Shift+R
  • Linux: Ctrl+Shift+R

There are other filename tricks one can use to avoid this issue (mentioned in comments of the OP). These are especially important in production where you have no control over browser behavior.

For non-Static Flask responses you can set the cache_control.max_age property, which should tell the browser when to expire the response if it is cached. For instance if you have a Flask XHR endpoint that returns JSON data you could do this:

@app.route('/_get_ajax_data/')
def get_ajax_data():
    data = {"hello": "world"}
    response = jsonify(data)
    response.cache_control.max_age = 60 * 60 * 24  # 1 day (in seconds)
    return response

You typically can also set default values in your production web server configuration for specific resource types (e.g. CSS/JS/HTML/JSON/etc)

Edit 4/1/2019 (unrelated to April Fools day)

  • Mac / Safari keystroke now appears to be: Cmd+Opt+R (via comments, thanks!).
  • See the new answer from @MarredCheese for a very elegant "filename trick" to force the browser to ignore cached copies for updated files.
Venge answered 14/12, 2016 at 15:43 Comment(3)
Mac change on 2019 • Cmd+Opt+RCupidity
thanks @CarlosGaleano . . . this seems to be specific to Safari from what I can tell, but added an edit here to mention this.Venge
They get April Fools as early as January in the US. 😉Lacielacing
O
42

Caching is normally good, so it's not advisable to eliminate it entirely. And using control + F5 or whatever to do a hard refresh is obviously not a scalable solution since you have to do it in every browser on every computer.

A better idea is to let browsers cache the files most of the time, but not right after they've been updated. You can achieve this by appending the time that the source file was last updated as an argument to its path URL. For simplicity, you can use the most recent modification time for any file in the static folder (or any subfolders), as opposed to looking at each file individually.

Python

def dir_last_updated(folder):
    return str(max(os.path.getmtime(os.path.join(root_path, f))
                   for root_path, dirs, files in os.walk(folder)
                   for f in files))

@app.route('/my-site')
def my_site():
    return render_template('my-site.html',
                           last_updated=dir_last_updated('mydir/static'))

Jinja Template

<script type="text/javascript" src="/static/my-script.js?u={{ last_updated }}"></script>

HTML Result

<script type="text/javascript" src="/static/my-script.js?u=1547330602.31"></script>
Ondrej answered 12/1, 2019 at 22:38 Comment(1)
This is the best answer for websites in production where users have a cached version of the files.Selfcontradiction
E
25

If you are serving your static assets with Flask (this is typically the case in a development environment), then you might need to set the SEND_FILE_MAX_AGE_DEFAULT configuration value:

Default cache control max age to use with send_static_file() (the default static file handler) and send_file(), as datetime.timedelta or as seconds. Override this value on a per-file basis using the get_send_file_max_age() hook on Flask or Blueprint, respectively. Defaults to 43200 (12 hours).

Solving this can be as simple as updating the app.config dictionary, like so:

app = Flask(__name__)
...
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0

If you do that, your browser will not cache static assets that are served by Flask.

Elkins answered 14/12, 2016 at 19:22 Comment(0)
F
3

Use:

if __name__ == '__main__':
    app.config['TEMPLATES_AUTO_RELOAD'] = True
    app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
    app.run(debug=True)

You can update using F5 or CTRL + R.

Favouritism answered 2/11, 2020 at 16:1 Comment(0)
H
-3

USE CTRL+ f5 to refresh the browser

Horney answered 22/5, 2020 at 10:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.