24

In my project I have a lot of Ajax methods, with external client-side scripts (I don't want to include JavaScript into templates!) and changing URLs is kind of pain for me because I need to change URLs in my Ajax calls manually.

Is there is some way to emulate the behavior of {% url %} templatetag in JavaScript?

For example, print urlpatterns starting with ^ajax and later in scripts replace patterns with their actual values?

That's what on my mind, and my question is - are there any common practices to do things like that? Maybe some reusable applications? Also I will be happy to read any advices and relevant thoughts you have.

Update 1: I'm talking about computed URLs, not static ones:

url(r'^ajax/delete/(?P<type>image|audio)/(?P<item_id>\d+)/from/set/(?P<set_id>\d+)/$', 'blog.ajax.remove_item_from_set'),
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dir01
  • 2,120
  • 4
  • 18
  • 17
  • Does this answer your question? [Calling Django \`reverse\` in client-side Javascript](https://stackoverflow.com/questions/2724383/calling-django-reverse-in-client-side-javascript) – Flimm Nov 24 '20 at 08:20

9 Answers9

19

Try creating javascript helper functions (in django template) for generating url string. In simple form they could look like this:

function generete_some_url(id){
    return "{% url some_url itemid=112233 %}".replace("112233", id);
}

Maybe this has some other implications but I think it should work.

kodmasin
  • 357
  • 2
  • 7
  • You save my life, thank you! Just one observation, i guess that is better to use id=0 in the placeholder because you'll never have one equals 0, we couldn't say the same for 112233, but in the other can have all 0 of the string changed, maybe we should one some template to change in the right position. – Rafael Capucho Oct 03 '13 at 22:10
  • 1
    I am glad you like my solution. Regarding id it is more important that value you are replacing is not somewhere else in url string. So it does not really meter is it 0 or 112233 as far as the same value is not elsewhere in url string. – kodmasin Dec 07 '13 at 21:45
14

What's wrong with putting JavaScript in your templates?

You often want to call an initialisation function in your HTML template anyway, so why not pass it an object containing URLs you'll be using?

<script>
MYGLOBAL.mymodule.init({
    fancy_ajax_url: '{% url fancy %}',
    fancier_ajax_url: '{% url fancier %}'
});
</script>

If you find yourself passing a lot of variable this way, or wanting to use logic in your JavaScript that you do in your HTML templates, then why not render your script through Django's templating engine? Remember, Django templates are not just for HTML documents - often it helps to use templates for plain text, XML, JSON, and yes even JavaScript. Worried about performance? Then cache the result.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ozan
  • 9,285
  • 4
  • 30
  • 27
  • That are initials of my task, so face it ) JS files would be delivered thru CDN. What I can do is to put urlpatterns on the top of my page (set js variables) and compile real urls from regexps with real data – dir01 Nov 25 '09 at 10:20
  • 4
    The downside of this approach is that there's no way to use view arguments. – intgr May 09 '12 at 09:31
  • 1
    By design, mixing languages is a bad idea. – azmeuk Jun 21 '16 at 11:02
  • This is vulnerable to XSS attacks - the safe way to pass variables to JavaScript in Django templates is the json_script filter: https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#json-script – Adam Johnson Feb 07 '20 at 18:56
12

I created a mechanism that builds a list of url patterns in your Django project and outputs that in a Javascript file. It is a fork of django-js-utils.

The repo link is here: https://github.com/Dimitri-Gnidash/django-js-utils

Dimitri Gnidash
  • 121
  • 1
  • 2
10

https://github.com/mlouro/django-js-utils

dutils is a small utility library that aims to provide JavaScript/Django developers with a few utilities that will help the development of RIA on top of a Django Backend.

It currently supports the following features:

  • Reverse method for generating Django urls...
gnat
  • 6,213
  • 108
  • 53
  • 73
dir01
  • 2,120
  • 4
  • 18
  • 17
  • Also check out some of the forks. Dimitri-Gnidash builds the URLs with a management command. ljosa created a view which builds them on the fly. – SystemParadox Feb 08 '12 at 15:33
  • It seems like this solution still requires to hard code the {url_name:pattern} dictionary. would be nice to automate the generation of the list found in dutils.conf.urls.example.js??? – Fydo Sep 05 '13 at 13:57
7

We created a small app called django-js-reverse for this purpose.

For example you can retrieve a named url

urls.py:

url(r'^/betterliving/(?P[-\w]+)/(?P\d+)/$', 'get_house', name='betterliving_get_house'),

in javascript like:

Urls.betterliving_get_house('house', 12)

result:

/betterliving/house/12/

ierror
  • 513
  • 2
  • 6
  • 13
4

What I usually do is put the URL in either an <input type="hidden" /> element, or in the rel="" attribute.

Then, when writing the JS (using jQuery below) I do:

$('div#show_more').click(function () {
    var url = $(this).attr('rel');
    // or
    var url = $('#more_url').val();

    $.get(url, function () { /* ... */ });
});

Nonstandard attributes are well supported by all major browsers and hidden elements don't have to be in forms.

Emil Ivanov
  • 37,300
  • 12
  • 75
  • 90
  • What would you do with computed urls? url(r'^ajax/delete/(?Pimage|audio)/(?P\d+)/from/set/(?P\d+)/$', 'blog.ajax.remove_item_from_set'), – dir01 Nov 25 '09 at 10:14
  • You put them somewhere on the page (in the django template) - the javascript is a separate file that is not generated by django. – Emil Ivanov Nov 25 '09 at 13:21
2

First, you should name your url:

url(r'^blog/(?P<item_id>\d+)/$', 'blog.ajax.remove_item', name='blog-item'),

Then you could pass urls as variables to your module:

<script src="{{ STATIC_URL }}js/my-module.js"></script>
<script>
$(function(){
    MyModule.init('{% url blog-item item.id %}');
});
</script>
// js/my-module.js
var MyModule = {
    init: function(url) {
        console.log(url);
    }
};

You could use tokens in your url:

<script src="{{ STATIC_URL }}js/my-module.js"></script>
<script>
$(function(){
    MyModule.init("{% url blog-item item_id='0000' %}");
});
</script>
// js/my-module.js
var MyModule = {
    init: function(url) {
        var id = 1;
        this._url = url;
        console.log(this.url(id));
    },
    url: function(id) {
        return this._url.replace('0000', id);
    }
};

Notice that your token should match the regex type to resolve successfully (I can't use {item_id} as token because it's defined with \d+).

I was a little bit unsatisfied with this solution and I ended by writing my own application to handle javascript with django: django.js. With this application, I can do:

{% load js %}
{% django_js %}
{% js "js/my-module.js" %}
// js/my-module.js
var MyModule = {
    init: function() {
        var id = 1;
        console.log(Django.url('blog-item', id));
    }
};

$(function(){
    MyModule.init();
});
noirbizarre
  • 3,429
  • 1
  • 30
  • 21
  • Using `{% url %}`, or any other tags that might add user data, is vulnerable to XSS attakcs - the safe way to pass data to JavaScript in templates is the `json_script` filter: https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#json-script – Adam Johnson Feb 07 '20 at 18:57
2

You can remove the parameters from the URL, and pass the dynamic parts as query parameters:

  $('#add-choice-button').on('click', function () {
    var thing_id = $(this).closest('.thing').attr('data-item-id');
    $.get('{% url 'addThing' %}?t='+thing_id, function (data) {
      ...
    });
  });
Bryce
  • 8,313
  • 6
  • 55
  • 73
1

I have found this cool django app called Django JS reverse

https://github.com/ierror/django-js-reverse

If you have a url like

url(r'^/betterliving/(?P<category_slug>[-\w]+)/(?P<entry_pk>\d+)/$', 'get_house', name='betterliving_get_house'),

Then you do

Urls.betterliving_get_house('house', 12)

The result is

/betterliving/house/12/
Kotlinboy
  • 3,725
  • 4
  • 16
  • 27