0

I tried to break this problem down into the simplest example. When the request is ajax, rendering the page with an updated context doesn't produce the expected result.

index.html:

<html>
    <body>
        {% if templateVariable %}
            <h1>{{ templateVariable }}</h1>
        {% endif %}

        <button id="testBtn">TEST</button>

        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script> 
        <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script>
        <script type="text/javascript">

            $(document).ready(function() {
                $(function() {
                    $('#testBtn').click(function(event) {
                        $.ajax({
                            type: "POST",
                            url: "./",
                            data: {
                                'x' : 'x',
                                'csrfmiddlewaretoken' : '{{ csrf_token }}'
                            }
                        });
                    });
                });
            });

        </script>
    </body>
</html>

views.py

def index(request):
    context = {}
    if 'x' in request.POST:
        context['templateVariable'] = 'I was updated via ajax'
        print('ajax ran')
        return render(request, 'index.html', context)
    context['templateVariable'] = 'I was not updated via ajax'
    print('ajax was not run')
    return render(request, 'index.html', context)
  • When I first load the page, 'ajax was not run' is printed, templateVariable is 'I was not updated via ajax', and the page renders with that in the h1 tag as expected.

  • When I click the testBtn, I expect the ajax request to trigger the if statement, update the context, and render the page with 'I was updated by ajax' in the h1 tag.

  • Instead, 'ajax ran' is printed but templateVariable remains 'I was not updated by ajax' when the page is rendered. 'ajax was not run' only is printed once when the page is loaded initially.

Why would I not be getting the expected result?

EDIT: It seems everyone agrees that you cannot return a render and update context variables with an ajax request but I'm still having trouble with this as I believe this is possible. Here's some alternate code:

index2.html:

<html>
    <body>
        {% if templateVariable %}
            <h1>{{ templateVariable }}</h1>
        {% endif %}

        <h1 id="placeHolder"></h1>

        <button id="testBtn">TEST</button>

        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script> 
        <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script>
        <script type="text/javascript">

            $(document).ready(function() {
                $(function() {
                    $('#testBtn').click(function(event) {
                        $.ajax({
                            type: "POST",
                            url: "./",
                            data: {
                                'x' : 'x',
                                'csrfmiddlewaretoken' : '{{ csrf_token }}'
                            },
                            success: function (data) {
                                $('#placeHolder').html(data);
                            },
                            dataType: 'html'
                        });
                    });
                });
            });

        </script>
    </body>
</html>

views2.py

def index2(request):
    context = {}
    if 'x' in request.POST:
        context['templateVariable'] = 'I was updated via ajax'
        print('ajax ran')
        return render_to_response('notIndex.html', context)
    context['templateVariable'] = 'I was not updated via ajax'
    print('ajax was not run')
    return render(request, 'index.html', context)

notIndex.html:

{% if templateVariable %}
    {{ templateVariable }}
{% endif %}
  • In this example, the page is initially loaded with templateVariable in the context as 'I was not updated via ajax'.
  • When testBtn is clicked, the ajax request triggers the if block in the view. This renders notIndex.html with the updated context.
  • The success function of the ajax call sets the generated html from notIndex.html to the h1 tag.

So why is it only possible to trigger a page render with ajax if the page is not the same one that the ajax call came from?

Community
  • 1
  • 1
  • 1
    Because using ajax means you don't refresh the whole page in the browser, which means the page is not (re)rendered by Django and thus is not being updated. To update the page with data from an ajax request, you have to update the html yourselves using javascript. – Landcross Oct 31 '19 at 15:31
  • The `if` in the index.html gets evaluated when the page first loads. If you want to change something after loading the page. You can not do that with django-template. You must do that with JavaScript. – Shakib Hossain Oct 31 '19 at 15:42
  • Seems like the traditional approach is to render another page with the updated context and set the innerHTML of some placeholder object on index.html to the html generated on that other page in the success/complete function. In this case, that other page's context is updated every time render is called. So why wouldn't the context update when you render the same page? – Tristan_Rogers Oct 31 '19 at 15:52
  • Because you have *specifically told the browser not to* by choosing to use Ajax. That is the whole point. – Daniel Roseman Oct 31 '19 at 15:58

1 Answers1

0

You can't return renders or redirects from AJAX, that's how it works

If you want to update your UI based on something that happens on the server say you have a cart and you'd like to implement 'add to cart' without refreshing the page, the 'add to cart' button must request an endpoint you provided in your urls.py & that url must return true if object is added, or false if it wasn't, but it won't update it on the UI, you need to manually change the cart items count with Javascript.

If you try to redirect or return a render to ajax, it will get the HTML or the redirect/render, nothing else.

if you want to redirect, you'll want to do that with JS but with no context variables from django.

see this ref

Ahmed I. Elsayed
  • 2,013
  • 2
  • 17
  • 30
  • What about the case where an ajax request triggers a view function that renders an alternate html page with some context variables. In the ajax success function you set a placeholder object's html on the original page to the returned data (which should be the html of the alternate page). In this case, every time the ajax call is made, render() is returned with updated context variables. But then trying to render the original page with updated context variables doesn't work? That's where I get confused – Tristan_Rogers Oct 31 '19 at 16:23
  • It's confusing because you can't return renders or redirects to ajax, you should only return responses, If I understood you correctly, It's because when you make an ajax call to an endpoint, the endpoint calls it's class view or function & does the code inside till it reaches return render, it actually returns the render, but it can't be rendered because that's not how ajax works, if you asked about something else, feel free to explain what you don't understand – Ahmed I. Elsayed Oct 31 '19 at 17:09
  • It's not possible to trigger a render either render the same page or another page, **BUT** you receive the HTML of the page through `return render()`, you can do whatever you want with the HTML – Ahmed I. Elsayed Oct 31 '19 at 18:56
  • btw when using ajax, don't stringify any keys in any dictionary, So you should post csrfmiddlewaretoken : 'csrfmiddlewaretoken' – Ahmed I. Elsayed Oct 31 '19 at 18:58
  • and x: 'x' not 'x': 'x' – Ahmed I. Elsayed Oct 31 '19 at 18:58
  • You can get the HTML of the page returned as a render, or even the HTML of the redirect page, but you can't go the redirect page itself, you can only get it's source (as if you requested it using a request method) – Ahmed I. Elsayed Oct 31 '19 at 18:59
  • if you want to send a variable to your web page using python, make an endpoint say path('myendpoint/variable/', views.send_variable) and make a function called send_variable that returns your variable, now ajax call this endpoint & you will get your variable inside the browser as response.data in javascript/jquery then utilize it – Ahmed I. Elsayed Oct 31 '19 at 19:01
  • The original application this issue came up in was when I needed to send a couple js arrays to my view function, use them to generate a csv response and then return the response triggering a download for the user. I wanted to use ajax since I was sending arrays but I noticed that triggering the function this way made it so returning the csv response didn't trigger any download. Then I noticed returning a render didn't work either. I had only used ajax to trigger interaction with the database and I used it the same way as in the second example to populate autocomplete search results. – Tristan_Rogers Oct 31 '19 at 19:51