3

let's say one of my urlpatterns looks like this.

url('^objects/update/(?P<pk>\d+)$', views.UpdateView.as_view(), name = 'update-object'),

I need to redirect user to the update page depending on the selected object (the list of objects is populated using Ajax). So I'd like to pass that named url pattern to the JavaScript, in order to build the actual url on the client side.

Example of what I want to achieve:

  1. pass the name 'update-objects' to the function
  2. get the actual url pattern, replace (?P<pk>..) with {pk}
  3. pass the result to the javascript, resulting in : objects/update/{pk}

any tips? thanks


to make it more clear: at the moment of rendering, I can't do url reverse because the PK is not known yet. I need to make kind of javascript-urlpattern which will later be converted to the real url (i.e. my JS code will replace {pk} part with the actual pk value)

migajek
  • 8,524
  • 15
  • 77
  • 116
  • Perhaps I am misunderstanding the question, but why not just translate **objects/update/(?P\d** to something like **objects/update/?pk?** and let the client-side js figure out how to replace ?pk?. What you need to do in that case is just fool django's url reverse with some dummy values that match the regex, replace the dummy with ?pk? (or similar) and then store url template in a js variable somewhere. This remark is less aimed at the OP than at the other answers. Actually, instead of ?pk?, could use a marker that plays well with sprintf.js. – JL Peyret Oct 23 '15 at 02:06
  • Does this answer your question? [Calling Django `reverse` in client-side Javascript](https://stackoverflow.com/questions/2724383/dry-urls-in-django-javascript) – Flimm Nov 24 '20 at 08:18

4 Answers4

5

The actual URL reversing must happen on the server side. There are several ways to do this, and the most elegant of these probably depends on how exactly your script and markup are set up for this. One thing I've done recently is to attach the URL to a logical element using HTML5 data attributes, which are easy to retrieve using jQuery. If you're not using jQuery, I'll leave it up to you to translate to pure JS. You haven't provided any code or specifics for your client-side, so I'm kind of shooting in the dark here, but maybe this will give you the idea:

Django HTML template:

<ul class="object-list">
 {% for object in objectList %}
  <li data-update-url="{% url update-objects object.pk %}">object.name</li>
 {% endfor %}
</ul>

JS:

$('.object-list').on('click', 'li' function () {
  var updateUrl = $(this).data('update-url')
  ...
});
acjay
  • 34,571
  • 6
  • 57
  • 100
  • thanks, but this is not the case. The source is JSON api, and the only information I have is the object's PK. I can't reverse the url while rendering the template, because at this point the PK is not available (not known yet). What I can do is to call reverse with some dummy PK (9999) which hopefully is not present in the urlpattern and then do string replace 9999 => {url} which I'll LATER (when object's PK is known) replace with the proper PK. Quite complicated, huh? ;) – migajek Jan 30 '13 at 17:34
  • +1 for using the data attribute. Definitely makes this stuff more elegant! – Aidan Ewen Jan 30 '13 at 18:40
  • @migajek You could also change your view function to make `pk` optional, and that way you can reverse it without knowing it ahead of time, and simply append it to the end of the URL. But doing a dummy reverse isn't a horrible idea either, IMO. It's a simple replace. For DRY, maybe pass the placeholder token in the JSON so that your script doesn't have to care what your actual token is. The fact that the placeholder could also be a real pk feels unsettling to me, but probably isn't that big of a deal. – acjay Jan 30 '13 at 20:35
2

It sounds like you need to make an additional ajax call once the object has actually been selected. Don't try and second guess your url.conf by trying to work out the url on the client side - you'd just be making trouble for yourself later. Wait till you can get a pk, then use django's reverse function to give you your url (doing anything else violates DRY).

How about creating a simple view that returns the url -

from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseBadRequest

def get_url(request):
    if request.is_ajax() and request.method == 'POST':
        obj_id = request.POST['obj_id']
        url = reverse('object-update', kwargs{'pk': obj_id})
        return HttpResponse(obj_id)
    return HttpResponseBadRequest()

Then write a javascript function that gets the url using an ajax call to your new view and then redirects. You'd call this function as soon as the object's been selected. I would suggest using JQuery to do this, pure javascript will require you to write more code, and probably write browser specific code (depending on your target). Also it supports dealing with django's csrf protection (you'll need to implement this for ajax calls if you haven't already).

var redirect = function(obj) {
    $.ajax({
        url: '/your-get-url-view/',
        method: 'post',
        data: {'obj_id': obj},
        success: function(url){
            window.location = url;
        }
    });
}

I'm afraid I don't know how you're getting from the selected object to the pk (For simplicity I've assumed it's available to the redirect function) - you may have to do some processing in the view to get there.

I haven't tested the above code, but it should give you an idea of what I'm suggesting.

Aidan Ewen
  • 13,049
  • 8
  • 63
  • 88
  • I've thought of this, this would be very easy to implement as I'm using django JSON RPC service and jQuery client anyway. However I don't like the idea of resolving each url via separate request. This actually means I'll double number of AJAX requests. – migajek Jan 30 '13 at 22:59
  • Where is the pk coming from? if it's coming from the server then perhaps you could include the url in the json along with the pk (then attach it to a data attribute as in acjohnson55's answer). That would keep your http overhead down. – Aidan Ewen Jan 31 '13 at 07:34
  • It is coming from the server, but I can't include every possible url for that object (there is plenty of them) just because I might use it here or there. It must be built client side. – migajek Jan 31 '13 at 08:14
  • because that would pollute the api with tons of unnecesary in most cases attributes. – migajek Feb 01 '13 at 19:02
  • `kwargs{'pk': obj_id}` is not valid python – Grijesh Chauhan Apr 16 '15 at 17:12
1

Try this one:

Reverse method for generating Django urls
https://github.com/mlouro/django-js-utils

One more
https://github.com/Dimitri-Gnidash/django-js-utils

catherine
  • 22,492
  • 12
  • 61
  • 85
0

If you have a URL that only has one PK field in it, you could resolve it with any number (e.g. 0), then substitute the number as required.

In my scenario my URL had a pk then an upload_id, so I had to replace on the right most instance of a 0, with <upload_id>, which the JS would replace this string occurance as required:

detele_url_upload_id_0 = reverse(f'{APP_NAME}:api_upload_delete', args=[pk, 0])
prefix, suffix = detele_url_upload_id_0.rsplit('0', 1)
context['generic_delete_url'] = prefix + '<upload_id>' + suffix

Then in the JS:

const deleteUrl = genericDeleteUrl.replace('<upload_id>', uploadId)
run_the_race
  • 1,344
  • 2
  • 36
  • 62