9

There are LOTS of post and pages discussing the use of Django and AJAX, and I've read hundreds over the past day or so looking for the answer to this question. A quick overview:

May of the examples show a hard-coded URL like this:

$.post("/projects/create/", {"name" : name}, function(data) {...

or some use the URL template tag, but with no parameters:

$.post("{% url create_project %}", {"name" : name}, function(data) {...

However, I'd like to include a Django-style parameter in a URL. Here's my url definition:

url(r'ajax/entity_name/(?P<pk>\w+)/$',EntityAjaxView.as_view(),name='entity_name'),

Yes, I'm using a class based view, and it is based on DetailView. This view looks by default for a pk value to be provided in the URL, and in a normal template I would use:

{% url entity_name id_number %}

to provide a link. In my code, I want to grab the value entered in an input box for the pk value. Here is a snippet of my JavaScript (which doesn't work):

var id_number = $('#id_endowmententity_set-' + rownum + '-id_number').val()
$.ajax({
    type: "GET",
url: '{% url entity_name id_number %}',

So, my question is, can I use the URL template tag with a value from an input box?

(I know that I could use POST instead of GET and pass the id_number in the POST data, but that won't work well with the DetailView.)

Sunderam Dubey
  • 1
  • 11
  • 20
  • 40
Dashdrum
  • 657
  • 1
  • 4
  • 9

4 Answers4

17

Django is a server-side application. Javascript is client-side. Django templates get rendered on the server, so {% url entity_name id_number %} is evaluated on the server side, and then it's value is returned to the client. Just because of this, it's impossible for you to combine Django templates with javascript. However there are couple of things you can do to solve your problem.

Since you are making an ajax call, and the ajax call depends on some user input, usually the best route for the client to send any type of user input to the server is by either using querystring (thing after ? in the URL) or by sending a POST data. So the simplest thing is to change your your url not to include the pk in the url, but for the view to get that as part of GET or POST data.

url(r'ajax/entity_name/$', EntityAjaxView.as_view(), name='entity_name'),

and the view (sorry I'm not familiar with class based views):

def entity_name(request):
    pk = request.GET.get('pk')
    ...

That seems to me to be the most elegant solution. If however you absolutely need to construct the url on the client side, you can generate a template url on the server side and then replace whatever parts you need on the client side to get the full url. This however requires more maintenance and therefore is more error prone. Simple js example of this approach:

var id_number = $('#id_endowmententity_set-' + rownum + '-id_number').val(),
    url = '{% url entity_name 0 %}'.replace('0', id_number);
$.ajax({
    type: "GET",
    url: url,
    ...
});
miki725
  • 27,207
  • 17
  • 105
  • 121
  • I tried using ajax callback with a named URL from my **function view** like `err_key="{% url 'pcd_list' %}"` and use it to create a link on the calling page using `$('#ajxErr').html(var_err + ' Check');` **where** `var_err_url` is a variable for the `err_key` value received back from Ajax call. **But it is failing.** However, if I use `err_key="path_generated_from_named_url"` in my function view, it works. Is there a way to use named url like I tried **in the first example above**? – Love Putin Not War May 18 '20 at 09:55
3

It is possible to set an Ajax url on the element you are selecting using an attribute and it will behave like Django urls. Importantly, you can even access the url in Javascript file. I use it a lot HTML

<div class="card-body" id="js-products" data-url="{% url 'chart-data' %}">
    <div class="chart-area">
      <canvas id="testChart"></canvas>
    </div>
  </div>

Note: the data-url attribute set on parent div JAVASCRIPT

$(document).ready(function () {

var endpoint = $("#js-products").attr("data-url");
var defaultData = [];
var labels = []
$.ajax({
    method: 'GET',
    url: endpoint,
    success: function (data) {
        labels = data.labels
        defaultData = data.data_default
        setChart()


    },
    error: function (error_data) {
        console.log(error_data)
    }
})

function setChart() {
    var ctx = document.getElementById('testChart').getContext('2d');
    var myChart = new Chart(ctx, {
        type: 'line',
        responsive: true,
        data: {
            labels: labels,
            datasets: [{
                label: 'Monthly Performance',
                data: defaultData,

            }]
        },
        options: {
            scales: {
                yAxes: [{
                    ticks: {
                        beginAtZero: true
                    }
                }]
            }
        }
    });
 }
});

DJANGO VIEWS Am using django rest framework class view but you can use either of function or class based view class ChartData(APIView):

authentication_classes = []
permission_classes = []

def get(self, request, format=None):
    labels = ['Products', 'User', 'May']
    data_default = [SeedProduct.objects.all().count(),
                    User.objects.all().count(), 4]
    data = {
        'labels': labels,
        'data_default': data_default,
    }

    return Response(data)

DJANGO URLS: import the view class from views

path('api/chart/data', views.ChartData.as_view(), name="chart-data"),
  • I think this is the best answer as of today for a lot of uses cases. For instance I have that I need to populate with ajax calls. For this, I use the data-url on the very table which needs to be populated this way. Easy to fetch & use in jquery. Just make sure you protect your endpoints against unauthneticated calls. – logicOnAbstractions May 20 '21 at 14:23
0

It's pretty time consuming to go round trip to a server just to fetch a URL. The best strategy to keep URLs dry and avoid this is to generate javascript that emulates Django's native url reverse function and then serve that code statically with the rest of your client side JS.

django-render-static does just that.

bckohan
  • 203
  • 1
  • 6
0

This worked for me. my URL was:

path('myurl/<str:type>', views.myfunction, name='myfunction')

my views.py file:

def myfunction(request,type):
    return render(request, "payment.html", context)

In my template, I solved the issue by:

<button type="button" class="btn"
    onclick="myfunction('forward');">My Button Name
</button>

<script>
function myfunction(type){
    let url = "{% url 'appName:myfunction' 'ok' %}".replace('ok', type);
    $.ajax({
      method: 'POST',
      url: url,
      data: {
          csrfmiddlewaretoken: '{{ csrf_token }}'
      }
  });
  }

</script>
Shahzaib Butt
  • 31
  • 1
  • 6