Cory
Cory

Reputation: 24260

Correctly accessing django static files from external javascript

I've got a django application using AngularJS with a bunch of JavaScript and template files.

In my django template I can use the {% static %} tag to properly reference those files like so:

<script src="{% static "angular/myapp.app.js" %}"></script>

However, the external files themselves obviously do not get resolved through django's templating framework and so that's not an option. So what people most often do is just hard code the static path there:

$routeProvider.when('/', {
   // this works but is not ideal
   templateUrl: '/static/pages/some-angular-template.html',  
})

I have seen recommendations of loading STATIC_URL into javascript somewhere and using that to construct references. Something like this:

Django template:

var STATIC_URL = {{ STATIC_URL }};
function getStaticUrl(templatePath) {
  return STATIC_URL + templatePath;
}

External JS:

$routeProvider.when('/', {
   templateUrl: getStaticUrl('/pages/some-angular-template.html'),
})

This is a bit better, but still not perfect because it only handles the base path. If you want to use something like ManifestStaticFilesStorage (which I do) then you still don't get the proper resolution of the file.

Is there any good solution to this problem? Options I am considering:

Just wondering if there's a standard practice or library that addresses this issue? I have run into this problem several times and never found a satisfactory solution.

Upvotes: 6

Views: 1439

Answers (2)

David Winiecki
David Winiecki

Reputation: 4203

Other option 1

Use your getStaticUrl function, but make it also append a query string like ?v=abc, where abc is unique for every deployed version of your app, to bust caches. (For example, your html defines window.ASSET_VERSION = 'abc' and when you deploy a new version of your app then your html defines window.ASSET_VERSION = 'xyz'.)

Advantages:

  • Don't need a huge global dict.
    • Don't need to maintain it.
    • Less knowledge about your app is visible to users who are looking for it (including malicious users) in one place (if that matters). Might save on server costs.

Disadvantages:

  • During a deploy of a new version of the app, when a user visits the site, then their browser will receive new static assets and old html, and the mismatch may cause the user to experience bugs.
  • If you use a CDN, like CloudFront, then you must configure it to include query strings in the cache key.
  • Probably makes A/B testing impossible?
  • When a new version of the app is deployed, the new static assets must be uploaded before the new html servers are deployed, or old static assets will erroneously be used by some of your users for a long time. (If a user visits your site during a deploy, their browser will receive new html and old static assets, will cache those old assets with the new query string, and will continue to use those old assets until the user manually clears the cache, the cached response expires, or the next deploy happens.)

Other option 2

Similar to "Creating an API to get the URLs": Put a dictionary containing the URLs in a static json file and make your getStaticUrl function return a Promise that resolves to the requested url after it fetches the json file. Put a version id in the json file name or in a cache busting query string (see above) so that the json file can be cached.

Advantages:

  • Performance might be better because the URL data is cached in the browser.
  • Might save on server costs.

Disadvantages:

  • All JavaScript that wants one of these URLs must by async. (Probably a deal breaker.)
    • Software engineers must spend more time (expensive).

Upvotes: 1

Cory
Cory

Reputation: 24260

My current solution (which works fine if not the most elegant) is just creating a huge global dict of constants in the django template and then referencing them directly in JS.

Django Template:

<script type="text/javascript">
    NG_STATIC_FILES = {
       "HOME": "{% static '/pages/home.html' %}",
       "SOMETHING_ELSE": "{% static '/pages/some-angular-template.html' %}",
    };
</script>

External JS:

$routeProvider.when('/', {
    templateUrl: NG_STATIC_FILES.SOMETHING_ELSE,
})

etc.

Upvotes: 4

Related Questions