0

The problem I face has to do with re-rendering a Django view based on a context that is updated by an AJAX post request that is initiated by jQuery. The reason why I need an AJAX request is because I need to modify the page UI without refreshing the page, which is critical to what I want to build.

So far, I am able to trigger the AJAX post request to the URL of the same page where the update is supposed to occur, and the Django view.py adequately registers that it has been called. However, although I can reproduce the ability to update the Django view's context, the view does not seem to re-render an updated HTML based on the updated context.

The thread at How to re-render django template code on AJAX call seems to describe exactly my problem. The top-voted solution of this thread is to have a conditional that is only triggered in case of an AJAX call that renders only a partial template (not the full HTML page) - a partial template that corresponds to the component to be updated. This is what's being reproduced in the code snippets below, but the HTML does not change based on the updated context.

Attached are the relevant code snippets for a simple attempt where the page displays <h1>2<h1/> by default and is meant to be updated to 5 when we click anywhere on the window. Clicking anywhere on the window triggers the AJAX call, but the page is not updated with <h1>5<h1/>.

view.py

def index(request):
    context = {"num": 2}
    if (request.method == "POST"):
        print("View hit due to AJAX call!") # // THIS CAN BE TRIGGERED ADEQUATELY!
        context["num"] = 5
        return render(request, 'num.html', context)
    return render(request, 'override.html', context)

override.html

{% load static %}

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="{% static 'js/num.js' %}" defer></script>
        <title>Title</title>
    </head>
    <body class="">
    <div class="">
        {% include 'num.html' %}
    </div>

    </body>
</html>

num.html

<h1>{{ num }}</h1>

num.js

var postUrl = "http://localhost:8000/";

function getCSRFToken() {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            if (cookie.substring(0, 10) == ('csrftoken' + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(10));
                break;
            }
        }
    }
    return cookieValue;
}

$('html').click(function(){
    values = [1, 2];
    var jsonText = JSON.stringify(values);
    $.ajax({
        url: postUrl,
        headers: { "X-CSRFToken": getCSRFToken(), "X-Requested-With": "XMLHttpRequest" },
        type: 'POST',
        data: jsonText, // note the data, jsonText, does not really matter. The Django context gets updated to 5 instead of 2. 
        traditional: true,
        dataType: 'html',
        success: function(result){
            console.log("AJAX successful") // THIS CAN BE TRIGGERED ADEQUATELY!
        }
    });
});
Yiftrer
  • 37
  • 1
  • 3
  • In the past when i done ajax calls i usually have 2 views, one to change the data and another to retrieve the html pice of template that i want to update, the reason behind that is to get the html elements i had to use XMLHttpRequest() instead of normal Request. I must say i never used jquery for this. Do you already try to console log the result? – Luiz Jan 20 '22 at 16:26
  • Hey, thanks for the reply. Yes, if you look at the num.js file, there is a line ```console.log("AJAX successful") // THIS CAN BE TRIGGERED ADEQUATELY!``` that is adequately triggered. The view.py is hit, too, in the line ```print("View hit due to AJAX call!") # // THIS CAN BE TRIGGERED ADEQUATELY!```. Could you elaborate on your approach of having two views? My assumption as a Django newbie is that views are for rendering HTML templates, but you say that you have a view "to change the data"? – Yiftrer Jan 20 '22 at 16:36
  • So, what i mean by "Do you already try to console log the result" its to console.log(result) to see if there is a xml attached to response, if there is it's just manner of get the element by id and set it to result. With 2 views the view to change the data works just like api point that only returns a status, such as 200 or 400, depending of this status you call another url to get the new html pice to update your template. This may not be the best solution, so if your function returns xml it's a better solution – Luiz Jan 20 '22 at 16:43
  • Yeah, the ```result``` is adequately printed. The problem is that the kind of application I want to build relies on filtering of the ```queryset``` in the ```view.py```, so I must rely on an updated ```context``` to re-render the HTML - I don't think that getting the element by ID is what I'm looking for. Also, is it necessary to rely on XML? I was really thinking about using HTML due to it being supported by Django's template language and the fact that I simply don't know XML all that much. – Yiftrer Jan 20 '22 at 16:58
  • Sorry for mislead you, i was not remembering that XMLHttpRequest can work not just with XML so you can work with HTTP normaly. You have to get a element to insert the response data to change your page, the context that is been passed on views.py changes the num.html and this should be on your `success: function(result)` – Luiz Jan 20 '22 at 17:19
  • 1
    Ah, thank you for the response. It seems like the problem is solved - I definitively cannot re-render the Django template per se, but I can do some processing within the Django view, then render the response to the AJAX callback function. And then, like you suggested, I could simply modify the HTML accordingly based on the rendered response. I thought the ```render``` function of the view was simply for rendering HTML - turns out it could pass back a response to the AJAX call... – Yiftrer Jan 20 '22 at 19:36

1 Answers1

0

If this part of code

success: function(result){
            console.log(result)
        }

prints the content of num.html you can change the num.html to get h1 id as

num.hml

<h1 id="numberToChange">{{ num }}</h1>

and in your num.js

$('html').click(function(){
    values = [1, 2];
    var jsonText = JSON.stringify(values);
    $.ajax({
        url: postUrl,
        headers: { "X-CSRFToken": getCSRFToken(), "X-Requested-With": "XMLHttpRequest" },
        type: 'POST',
        data: jsonText, // note the data, jsonText, does not really matter. The Django context gets updated to 5 instead of 2. 
        traditional: true,
        dataType: 'html',
        success: function(result){
            document.getElementById("numberToChange").innerHTML = result}
    });
});
Luiz
  • 1,985
  • 6
  • 16