Forcing static files to never cache - django

I'm serving a Django app on top of Apache, and after my recent deployments, I noticed that a lot of users were being served the new version of the main HTML file, but they were still picking up the older JS files from their Cache.
I tried stuff like patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True), but this doesn't seem to work for static files.
Is there a way to always force every static to be re-fetched from the server when a page is accessed?

You can do a lot of stuff with your headers.
Or you could use something like {% static 'js/jquery.js' %}?v={{ settings.CURRENT_VERSION }}, this way the browser would think that you are using a completely new file, because the name of the file would be something like jquery.js?v=1. I would not really recommend doing this, but it is a really quick hack.

Related

Django: Make JS source maps compatible with staticfiles filename hashing

In our Django project we are using Gulp to compile our assets, then UglifyJS to minify them. During this whole process we are generating sourcemaps, which appear to be working correctly.
The problem comes when we use the Django static template tag to include our minified files. Say we have a minified JS file called ourapp.min.js. In our template we would write:
<script src="{% static 'ourapp.min.js %}"></script>
which would be compiled into something like:
<script src="/ourstaticroot/ourapp.min.0123456789ab.js"></script>
(where 0123456789ab is a hash of the file contents)
The problem now is that, although the file has been renamed, our sourcemap still points to the old filename, so suddenly becomes invalid. If we then need to debug this page (say, using Sentry) it cannot find the source file and we are left to debug the uglified file instead, which becomes much more of a task.
Does anyone know of a good way to get around this? We would like to continue using Gulp for our assets, and also continue using the hashed filenames, as this prevents issues caused by caching of stale asset files.
it does not really matter, since the original files are being kept. So if your file points to a map without a hash it should be served by Django. Of course you need to be careful with long term expiration headers. Should you be using whitenoise are are fine, since they handle expiration properly and long term expiration headers are only set on hashed files.
Cheers
-Joe
I see two options available to you. It's not clear where exactly the cache busting suffix gets added to your files, but at that point you could:
Use some string processing, to manipulate the assets and set their sourceMapUrl with a url that is generated by your system and includes the expected suffix //# sourceMappingURL=/path/to/file.js.<suffix>.map. This could be a simple bash command in a gulp step (if that's where it happens)
Alternatively, browsers will also accept an http header SourceMap: /path/to/file.js.map which you could instruct your asset server to set for files with headers. This may be more difficult depending on your infrastructure.

Grunt - change asset linking when building for production

I've built a mobile version of an existing website. The site I've built is completely static and relies only on HTML/CSS/JS. This site resides on the same server as the desktop version and is served from under http://example.com/mobile as a subdirectory. As such, I've set the <base> tag to:
<base href="http://example.com/mobile/">
The only access I get to the existing site is via FTP, so I don't have too many options to reconfigure the already existing server. And I don't want to either.
My mobile version is only somewhat functioning - all the <script> and <link> tags inside index.html correctly point to http://example.com/mobile/{js,css,img}, while links used inside js or css files look up to the top domain (e.g - http://example.com/img/image.png instead of http://example.com/mobile/img/image.png).
I can manually copy all the assets for the links to work, but since this is a Grunt project I figured there might be a smarter way to do this.
So, how can I have all internal asset links in js or scss files change when building (grunt build)? Something like:
site.css
// in dev
body {
background: url('/img/image.png') top left no-repeat;
}
// after building
body {
background: url('/mobile/img/image.png') top left no-repeat;
}
Thanks.
See grunt-replace or grunt-string-replace as tools that can help with this.
You could use either of these within your build task to process the changes you need.
The hardest part will be writing an accurate regex, but there are many regex testers/builders online that can help if you aren't expert at that.
Also, while .htaccess is a great idea for Apache based sites, it doesn't help if you are on Windows or on a cloud hosting solution that doesn't honor it.

Referencing images from template

I want to insert an image into my template. The image resides in the same folder as the template.
I tried:
<img src = "imageName.png" />
but for some reason, this wouldn't work.
Does anyone have an idea why that is?
Don't put images in the same folder as templates. Images are a part of static content. You should read about managing static files in django.
Hope that helps.
I'm guessing you're new to the whole django scene, so don't be afraid to hit a few boulders on the way. In fact, this is a problem that I faced too, since I just usually put everything on a web-server, with an index.php file (yes, the horror), and everything was just relative.
So, let me give you a little context, what you are trying to embed on a page is called a static file. What that basically means is a file that does not change, a file that is not dynamic. Now, since these static files require no processing, they are treated specially, and are put inside a static folder. You can see where your static folder is when you check your main settings.py file.
Now often, people make a lot of mistakes with static files, because there are so many variables that have a STATIC infront of them. I know, its totally counter-intuitive, but there are reasons why they're there. So, let me direct you to something that can be a little help in understanding this whole fiasco.
When using static files, you usually use a few special tags. You can learn more about them here. But, I'll just show you how you would embed your image into your html, just as a sample.
{% load staticfiles %}
<img src="{% static "images/myphoto.png" %}"/>
So, how should your directory look like? Well, what you would have is something like this
STATIC_FILES_FOLDER > IMAGES > myphoto.png
Hope that helps.

What are the pros and cons in serving CSS and JavaScript using Django template system

Often, I encounter scenarios where I see it makes sense to use template tags in CSS and JavaScript files, such as the use of {{ STATIC_URL }} in CSS to access image. I understand the only way to achieve this is to have CSS and JavaScript files served by Django. I am interested in this approach. But before I commit, I want to hear you experts' experience on it. What are the pros and cons of this approach? Thx.
Pros:
You can make a lot of per-request decisions about how things look and behave.
You can keep the number of different CSS/JS files to a minimum.
Cons:
Browsers tend to cache CSS and JS aggressively, so you'll need to use some aggressive anti-cache techniques. Of course, this means disabling caching for some/all static files.
Every CSS and JS request will consume another thread of your WSGI server. In a normal request/response cycle, each request generally takes up one thread; you're effectively tripling this, at least, so now your app that could handle 200 simultaneous requests now can only handle 66.
When your site makes it big, a CDN probably can't help you.
Alternatives:
Tweak the CSS via javascript, and set a javascript variable inside your page template to control the tweaks.
Use multiple CSS files and control their inclusion dynamically.
Generate static files as needed, but then cache them to disk and serve them via mod_xsendfile. This only works if you are serving static files from somewhere the django process can write to, such as on the same machine or a network mount.
Personally, I've been sticking with the Django team's advice to make CSS and JavaScript static files, served directly by the server instead of via Django. It hasn't been a problem and has simplified a lot of things. Generally, any time I think I need a dynamioc CSS or JS file, there's a way to refactor so I don't.
For example:
the use of {{ STATIC_URL }} in CSS to access image
I'm not sure how variable your {{ STATIC_URL }} is, but I've found that using the <base> tag in my pages fixes a lot of things. I assume this is for background images? Could you update your question to give an example?
Another thing I've done is, if my JavaScript needs dynamic data, I'll put most of the code in a JavaScript library I serve as a static file and then put the minimum dynamic stuff in a <script> tag at the end of the page. Usually I'll put it all in an object (looking a lot like JSON) and then just pass that object to a function. Come to think of it, you could just take all the dynamic stuff, make a dictionary out of it in your view function, encode it into JSON, and pass it via context. Then your page template just looks something like:
<html><head>
...
<script src="{{ STATIC_URL }}/js/foo.js"></script>
...
</head><body>
...
<script>
foo_main({{ foo_params_json|safe }});
</script>
</body></html>
This makes it a lot easier to reuse this code.

Django: include external include files

Im building a web app which will be part of an existing static website. I'd prefer to use the header and footer from the current site which are static .inc include files.
Is there a way to include these files something like:
{% include 'http://www.mysote.com/inc/footer.inc' %}
There isn't a built-in way to do this in Django, but it would be a really easy template tag to write on your own (there's a decent chance someone has already written such a thing, though a quick search didn't turn it up for me). If you want to go that route, you can do that with a quick simple_tag (documented here: https://docs.djangoproject.com/en/1.3/howto/custom-template-tags/#shortcut-for-simple-tags). It could probably be as simple as something like:
def include_external(url):
import urllib2
return urllib2.urlopen(url).read()
register.simple_tag(include_external)
{% include_external 'http://....' %}
However, as Umang mentioned, that is potentially problematic--fetching that include file will probably significantly increase your page load time, and you'll guarantee that a failure in your static site will bring down your Django app as well. If either of those things turns out do be a concern, you could look at caching the header--however, that's adding additional complexity, and you might be better of just copying your header file over each time it's updated.