Preventing errors from reverse URL for javascript

Hey,

Unsure how to phrase this but here’s my use case.

I’m making ajax calls based on some events in a table (row click/dbl-clicked). In order to avoid hard-coding URLs, I’m using a data-url attribute in my templates, like so:

      <tbody id="details"  data-url="{% url 'pickings:details' %}">

For instance in this case, the body of the table is initially empty. When I want to make an ajax query to populate it, jquery fetches the data-url attr of the #details element and appends to it the html rendered by the server. Works well.

However, there are instances where the reverse URL expects say an int:pk, like so:

    <table class="table table-bordered" id="table_list" width="100%" cellspacing="0" data-url="{% url 'pickings:detail_product' %}">

With the corresponding urls.py:

path('product/<int:pk>/', views.PickWarehouseDetailedView.as_view(), {"section": "product"}, name='detail_product', ),

Obviously, when rendering my template, django complains that it cannot find a reverse for
data-url="{% url ‘pickings:detail_product’ %}", since I don’t know which one it will be at that time.

My current workaround is to provide a dummy one & strip it when I make the ajax call (e.g. a row has been double-clicked, and there’s an associated product pk). However I would ideally like to:

  • Keep using the reserve URL the way I do to tell my jquery requests where to go
  • avoid messing around with stripping strings from URL
  • so more or less “silence” the Django error about not finding a reverse%

We render the template with a pk of 0, then in the JavaScript, replace the zero in ‘/0/’ with the pk from the item selected.
(We have a slightly different solution when the URL contains more than one variable needing to be replaced, but the idea is the same.)

1 Like

Okay, if Ken says that’s the way he does it, then there’s no other options. I guess in a way it wouldn’t make sense, like Django is warning me it cannot find an reverse in my template, and in general it is indeed something I’d want to know, so shutting down the warning might not be so well advised.

It still does feel a little hacky to me.

Not really the intent of my reply, and yes, I probably should have put more thought into it. (That’s what I get when I fire off a quick reply on my phone.)
There are other options, I even hinted at one when I made my reference to having multiple-replaced parameters.

Five other viable solutions are:

  • Match the url up to where the variables begin. Then, instead of relying upon Django to process the path components of the variables area, handle it yourself in your code. (There is nothing forcing you to have Django process a complete URL.)
  • Add the variables as GET parameters on the end of the URL
  • Create a separate “dummy” URL entry that doesn’t have the parameters - use that for the reverse function. Then instead of replacing URL components, you’re concatenating on to the end of the URL.
  • Render the complete URL as a data element for each selectable item, so that when the item is clicked, you use the URL from that item.
  • Define some API endpoints as fixed URLs, and not subject to change, making it unnecessary to use reverse.

Then, becoming absurd and not being presented seriously:

  • Create a URL-generation endpoint. Write a view that takes a URL name and appropriate parameters, return the reversed URL.

I’m fairly sure that at one time or another I’ve implemented or worked on a system using most of these. (But definitely not the last one.)
My opinion is that they all feel “hacky” in some way - and I think that comes from having to effectively split the processing of the URLs between both sides of the communications channel. (Django doesn’t know which item is going to be selected, and the JavaScript doesn’t have the information to be able to do its own reverse implementation.)

We settled on the original idea because, for us it ended up being the “easiest and most understandable solution.”

1 Like

Oh, it wasn’t meant as a criticism or anything wrt to your initial (indeed short) reply. More a mark of respect actually - if you haven’t found an obviously better, less “hacky” way to do this, then it probably means there isn’t. Or just at least that there’s little point in my investing much time in improving the approach.

Also somewhat comforting to know that using reverse url in this way (e.g. in html attribute in my template) to tell my JS code where to call is a legitimate way to do this. I was 95% it was fine, but as I don’t have ton of experience, I try to keep a healthy skepticism about those, let’s say, “non built-in” features I use/develop.

==============

Regarding the alternatives you elaborated - does show there is more to it than I initially thought. Just for the fun of thinking out loud, and perhaps others coming accross this will enjoy the thoughts…

Alternatives that involve modifying the url itself feel wrong. I think that a good url that clearly shows the user “where” he is in the application, and also that is bookmarkable (e.g. products/details/2321312/) is what I need to aim for as a dev. I find I’m not willing to compromise on that in order to achieve some goals that purely have to do with my job as a dev, behind the curtains.

I had considered the one about having a “dummy” endpoint w/o arguments. It felt more hacky to me - the view needs an argument to mean anything. Thus I feel that whatever happens, I should provide it with one.

And yes, endpoints should be fixed. But alas, I’m developing this agile-style, and thus I can’t guarantee that I’ll have the foresight to makes this robust & well-thought enough so that they won’t change. Even setting longer-term aside, while prototyping things, it’s nice to be fairly confident that by changing some url structure, I’m not breaking some ajax calls that may be hard to test for and go under the radar for a while, until later when I won’t be obvious to me why it’s broken… Which is actually the main reason I decided to figure out a way to use reverses in the template for this in the first place…

I think you’re very right in saying the issue arose from the “split” of the url processing. The way I see it at this point, is that the “split” occurs because I’m adding another component to the system (javascript/ajax calls). Therefore, if something’s got to give, it’s logical that the “added” component that “creates” the problem should be one which side I’ll work out the solutions (hacks).

Still just thinking aloud here - I do enjoy the mental exercise - but I’d like to add some comments to your thoughts:

I’m not understanding this comment. You’re not modifying any “seen” url. The URL you are referencing or being directed to is the final URL and can be bookmarked. The URL fragment and the work being done to it is never seen by the user.

In the situation where this was used, the dummy URL was defined to use a view returning a 404. It could never be used directly as an endpoint. It only existed as a url entry in the urls.py file, to be used as the name for a call to reverse.

Another project worked on - actually a conversion of a flask-project - all external APIs were prefixed by /a/ - it was a completely separate URL structure from the rest of the application. It’s a single urls.py file that maps a full URL to a view.
Those URLs are etched in stone and are not to be modified regardless of any other changes made to the application. (You can have more than one URL referring to the same view.) They were defined 5 years ago (6 now?) and are still valid. (Ok, so being honest, it’s only about a dozen URLs for which this was done. It’s not that much of a maintenance issue.)

I’m not understanding this comment. You’re not modifying any “seen” url. The URL you are referencing or being directed to is the final URL and can be bookmarked. The URL fragment and the work being done to it is never seen by the user.

My bad actually - I had always just sort of assumed that putting the variable within the GET parameters would means that products/details/2321312/ would become products/details/.

Thanks for the additional uses cases/experiences. I for one definitely can learn from that - helps broaden perspective and just be better able to think outside the box in the futur.