Search code examples
djangotemplatesdjango-templateshandlebars.jsdjango-staticfiles

How to reference static files in a handlebars-django template


Summary:

How should I reference static files in a handlebars-part in a django template? I can use handlebars if I use verbatim tags, but then I can't use django's static tag.

Details

While converting an app to Django, I came across a part that uses handelbars.js for rendering ajax-call-results. Via, amongst others, "Handlebars.js in Django templates" I found out about the {% verbatim %} tag.

A simple piece of handlebars works fine with this. But I also have a part where images are dynamically shown based on the result, which looks something like this:

<img src="path/{{ result }}.png">

Now while this works fine if I set the path manually, I believe in Django it is good practice to reference your static files like so:

<img src="{% static 'path/file.png' %}">

Just getting a static_url constant isn't advised, see for instance this blog

So unless someone has a real compelling reason to fix it otherwise, I believe it is best to use the {% static %} method.

The naive solution would be to combine the 2 techniques, and literally spray the template with verbatim/endverbatim. Apart from the fact that this looks ugly, illegible and seems like a bad idea from the start, it also doesn't work.

{% verbatim %}
    <!-- handlebars -->
    {% endverbatim %}
    <img src="{% static 'path{% verbatim %}{{ result }}{% endverbatim %}' %}">
    {% verbatim %}
    <!-- handlebars -->
{% endverbatim %}

This ends in tears, as the result is

TemplateSyntaxError at /
Could not parse the remainder: ''path{%' from ''path{%'

It might be possible to generate the correct static url on the backend side, and render that. But the backend shouldn't be aware of what image we want to show in the template.

Only solution might be to do an extra call to the backend with the 'relative' string (e.g. path/result.png) to the backend, and ask for the correct static link? This isn't that hard, but requires an extra call, which shouldn't be the case.

So how do I correctly reference these static files ?


Solution

  • You’d like to not delineate the boundary between handlebar tags and Django tags. Perhaps the cleanest solution is to explicitly declare handlebar tags as so:

    {{ "handlebars_variable"|handlebars }}
    

    where the filter handlebars is defined as so (source):

    from django import template
    register = template.Library()
    
    @register.filter
    def handlebars(value):
        # str.format would require ugly escaping, so we use '%'
        return '{{%s}}' % value
    

    But that’s not enough: You want to pass a handlebars tag to static, and even with the filter you can’t do that directly. But perhaps you could try using with:

    {% with "handlebars_variable"|handlebars as handlebars_tag %}
      <img src="{% static handlebars_tag %}">
    {% endwith %}
    

    But even that’s not enough. You want to prepend path/. There are multiple options for you:

    • You could use the add filter based on this answer and nested with statements (ugh).
    • You could define a template tag called setvar like done so here (if you’d like).
    • You could define an ad hoc template tag like this (perhaps inelegant):

      @register.filter
      def static_result_path(value):
          return 'result/{{%s}}' % value   
      

      and then change the template code to:

      <img src="{% static "handlebars_variable"|static_result_path %}">
      
    • Use get_static_prefix (the simplest!):

      <img src="{% get_static_prefix %}result/{{ "handlebars_variable"|handlebars }}" />
      
    • (And there’s always Jinja.)