Django javascript template tags with injected variables

Hello,

I would like to get a url using a url pattern with JavaScript (inside a script tag within a django html template file).

Generally, this is how you can do it:

var url = "{% url 'pattern_name' argument %}"

And if your argument is a variable like this, I found at least one hack that works:

var arg = 'my_argument'
var url = "{% url 'pattern_name' temp %}".replace('temp', arg)

(If someone knows a better way to do that, I’m all ears.)

But, I have yet to find any way to construct the url using the url pattern when you have the url pattern as a variable like this (contrived to be simple):

var urlPatternName = 'pattern_name'
var arg = 'my_argument'
// var url = ??????

I’ve tried using JavaScript template strings like this:

var url = `{% url '${urlPatternName}' ${arg} %}`

Error: Could not parse the remainder: '${urlPatternName}' from '${urlPatternName}'

I’ve tried using the hacky .replace like this:

var url = "{% url 'hackPattern' hackArg %}".replace(hackPattern, urlPatternName).replace(hackArg, arg)

(Just to be sure I tried a single replace for a url pattern with a url that does not take arguments, that didn’t work either)

Error: Reverse for 'hackPattern' not found. 'hackPattern' is not a valid view function or pattern name.

I tried building the strings with +, too.
Error: Reverse for '" + urlPatternName + "' not found. '" + urlPatternName + "' is not a valid view function or pattern name.

And so it appears that the url tag is processed before any template strings or string replacement or string building happen.

Any suggestions?

Thanks

Always keep in mind what happens where, and when.

Template rendering occurs in the server, before the page is sent out to the browser.

JavaScript executes in the browser, after the page has been rendered (and sent to the browser).

What you need to determine is when this replacement needs to occur, and under what circumstances it’s going to happen, and where this replacement value is coming from.

What you identify as the “hacky .replace” is, in essence what you need to do - just remember that the replace function call is going to be used on the rendered url.

So for instance, in your template, you might have something like:
var url = "{% url 'url_reference' 'url_arg' %}";

This might end up being rendered as:
var url = "/some/url/path/url_arg/";

Which means in your JavaScript, you could write something like:
var new_url = url.replace('url_arg', new_arg_value);

(Note: I’m winging this, so I may have the calling sequence wrong for that replace function, but hopefully it gives you some ideas.)

1 Like

Don’t do var url = "{% url ...}"; , or ever template JavaScript. It can look safe but it’s vulnerable to HTML injection. Always use json_script. See my article.

Personally I wouldn’t use {% url ... %} for this and just hardcode the URL path in the JavaScript. Django’s URL dispatcher docs start by pointing to the Tim Berners-Lee article Cool URI’s Don’t Change. Changing URL’s should be done rarely, and if done you should add redirects where possible. If you end up changing the URL’s your JS links to, you can faily easily search your code for references and update them, and moreover if you have some tests for your JS then they should catch it.

3 Likes

Aah, thanks for the link to that article. This is good to know. So if I understood right, if I’d like to have JavaScript in it’s own separate file and would like access to the same data that my template has access to, I should include something like this in the django html template (let’s say it’s a blog post rendered with DetailView):

<!-- post_detail.html -->
{{ post|json_script:"post" }}
<script src="{% static '/path/to/js/post_detail.js' %}"></script>

Then in the JavaScript I can access the post in a function like this:

// post_detail.js
function doSomething() {
  let post = JSON.parse(document.getElementById('post').textContent)
  let author = post.author
  // do something with post data
}

Mmm, yes about the URLs. I assumed it would be bad to hard code them, but I think that’s what I’ll do. You’re right, with tests there shouldn’t be an issue.

Yup your json_script example looks right.

Enjoy hardcoding your URL’s.

I might be misunderstanding part of the issue, but given your response I’d like to clarify something and get your opinion on it. If I needed a url to use in a javascript module I typically do something like:

<div id="container" data-posts-url="{% url 'post:list' %}">
</div>

Then (assuming jquery)

const container = $('#container') 
container.load(container.data('posts-url'))

This causes urls to be crafted in a consistent manner which makes searching for urls using parameters or nested urls easier to search for. Basic url patterns like my example are easy to find either way.

Yes using a data attribute also works alongside json_script, however json_script can pass all kinds of data so that’s why I point people to it.

The OP’s issue was wanting to use {% url %} with variables in JS though, which can’t work, and I’d nonrmally go for hardcoding at that point.

Thank you for elaborating.