最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Django: render staticfiles through template engine at deploy-time - Stack Overflow

programmeradmin0浏览0评论

I want to render some static files (*.js in particularly) using Django template variables. I believe this is a mon use-case, especially when doing anything AJAX-y; I don't want to hardcode AJAX urls in my .js files any more than I want to hardcode them in my .html files. Buuuut of course I don't want those static files to have to run through the template engine at every client request because this will be slow. I am referring to things like URLs (which do not change after pile/deploy) or static (non-db) model attributes. (I suppose there are use cases where these things might be changed at run-time - this is Python, after all- but I think they are unmon). For some possible template variables (e.g. model fields), of course the file must be rendered at the time of the client request, but this is not what I'm talking about.

So wouldn't it make sense to render some of my static files through the template engine, for a subset of possible template variables, perhaps at the same time as collectstatic?

As far as I can tell this is not currently the case. To be clear, what I am looking for is a solution to render static files through the template engine at pile/deploy-time so that at "client-request-time" they are in fact plain old static files.

Such an approach would avoid these hacks:

  • DRY URLs in Django Javascript
  • Using the Django URL Tag in an AJAX Call

Disclaimers:

  • Yes I know there are template engines out there for javascript (mustache, handlebars, prototype, etc). Why should I add another template engine to the stack when Django already has one? Plus the syntax collides! That seems silly.
  • This looks like it takes a crack at it, but it's plicated and not fully implemented.

So:

  1. Is there a solution out there that I am missing?
  2. If not, is there a way to hook into collectstatic (like a pre-collectstatic hook) that would allow one to render certain static files through the template engine before "collecting" them?

EDIT: No responses yet...is this a really dumb question, and I'm missing something obvious? If so...go ahead and let me know...

I want to render some static files (*.js in particularly) using Django template variables. I believe this is a mon use-case, especially when doing anything AJAX-y; I don't want to hardcode AJAX urls in my .js files any more than I want to hardcode them in my .html files. Buuuut of course I don't want those static files to have to run through the template engine at every client request because this will be slow. I am referring to things like URLs (which do not change after pile/deploy) or static (non-db) model attributes. (I suppose there are use cases where these things might be changed at run-time - this is Python, after all- but I think they are unmon). For some possible template variables (e.g. model fields), of course the file must be rendered at the time of the client request, but this is not what I'm talking about.

So wouldn't it make sense to render some of my static files through the template engine, for a subset of possible template variables, perhaps at the same time as collectstatic?

As far as I can tell this is not currently the case. To be clear, what I am looking for is a solution to render static files through the template engine at pile/deploy-time so that at "client-request-time" they are in fact plain old static files.

Such an approach would avoid these hacks:

  • DRY URLs in Django Javascript
  • Using the Django URL Tag in an AJAX Call

Disclaimers:

  • Yes I know there are template engines out there for javascript (mustache, handlebars, prototype, etc). Why should I add another template engine to the stack when Django already has one? Plus the syntax collides! That seems silly.
  • This looks like it takes a crack at it, but it's plicated and not fully implemented.

So:

  1. Is there a solution out there that I am missing?
  2. If not, is there a way to hook into collectstatic (like a pre-collectstatic hook) that would allow one to render certain static files through the template engine before "collecting" them?

EDIT: No responses yet...is this a really dumb question, and I'm missing something obvious? If so...go ahead and let me know...

Share Improve this question edited May 23, 2017 at 11:51 CommunityBot 11 silver badge asked Feb 9, 2014 at 18:15 andyandy 1,4593 gold badges15 silver badges33 bronze badges 2
  • 2 lol I'm up too late, read it as Django render sacrifice through temple. hahaha. – Chris Gunawardena Commented Mar 24, 2014 at 12:03
  • All three answers looked like workable solutions. Each one gets an upvote. The bounty goes to the most prehensive answer which also had the most upvotes (um, 1, but still) at the time of bounty awarding. Thanks all. – andy Commented Mar 24, 2014 at 18:08
Add a ment  | 

4 Answers 4

Reset to default 4 +50
  1. There are several frameworks for Django for same purpose: django-pipeline, django-assets, and etc. which integrates different static files processing strategies, with varying degrees of difficulty configuring.
    I use an external tool - Grunt (it requires node.js) - for asset post-processing after collectstatic. It is easier and has a lots of plugins for any purpose (source validation, css/js/images minification, merging, testing and etc.).

  2. It is possible to hook in collectstatic by a custom static files storage with overrided post_process method.

example/settings.py

STATIC_ROOT = 'assets'
STATICFILES_STORAGE = 'example.storage.MyStaticFilesStorage'

example/storage.py

import os
from django.contrib.staticfiles.storage import StaticFilesStorage
from django.core.files.base import ContentFile
from django.template import Template, Context


class MyStaticFilesStorage(StaticFilesStorage):

    def post_process(self, paths, dry_run=False, **options):
        # don't even dare to process the files if we're in dry run mode
        if dry_run:
            return
        js_template_data = {'foo': 'bar'}  # template variables
        js_template_extension = '.jst'
        js_extension = '.js'
        for original_name, (storage, path) in paths.items():
            processed = False
            saved_name = original_name
            original_path, original_extension = os.path.splitext(original_name)
            if original_extension == js_template_extension:
               with storage.open(path) as original_file:
                  saved_name = original_path + js_extension
                  if hasattr(original_file, 'seek'):
                      original_file.seek(0)
                  original_source = original_file.read()
                  c = Context(js_template_data)
                  saved_source = Template(original_source).render(c)
                  self.delete(saved_name)
                  self.delete(original_name)
                  self._save(saved_name, ContentFile(saved_source))
                  processed = True
            yield original_name, saved_name, processed

A pletely different way to approach the problem would be to ask if you really need to get those URLs in javascript--instead, can the Javascript get the URLs from things like data attributes in your HTML?

In other words, you might have wanted:

homepage.html:

<div id="pop-up-modal">pop me up</div>

homepage.js:

$("#pop-up-modal").click(function {
  $.ajax("{% url 'some-class-name %}") 
  ...
});

When it can often be more straightforward to do something like:

homagepage.html:

<div id="pop-up-modal" data-popurl="{% url 'some-class-name' %}">pop me up</div>

homepage.js:

$("#pop-up-modal").click(function {
  $.ajax($(this).data('popurl')) 
  ...
});

I think that django-medusa would suit your needs.

By setting up a renderer and using the disk based backend, generating the static files would be as easy as:

django-admin.py staticsitegen

You aren't crazy. I was frustrated by this as well and found myself hacking something together for each new Django project I tackled. I think the reason for the lack of direct solutions is that this is mega-drought bone DRY. Its super easy to just hard code these things and call it day. This and the two most mon use cases for this involve generating code in one language from code in another which tends to be viewed as suspect.

I've recently published a new Django package django-render-static that solves this problem generically. It piggy-backs on Django's existing template engine infrastructure. A management mand called render_static should be run before collectstatic. This mand will look for templates registered in settings (or passed as arguments) and render them to your static file location on disk. They're then available for whatever normal static file packaging pipeline you have configured.

I'm sure there are more use cases, but the two most mon I've found are providing a reverse utility in static JavaScript thats equivalent to Django's and auto-translating define-like python structures (i.e. choice fields) into JavaScript. The app provides template tags that do both.

The JavaScript url reversal code is guaranteed to be functionally equivalent to Django's reverse function. I won't bother plopping example code down here because it's been well documented.

发布评论

评论列表(0)

  1. 暂无评论