Django context not loading new items after POST

Viewed 24

I have a view in Django that shows statistics of various items within the app. Users can filter what statistics they want to see based on selections. Currently I use JavaScript to update the values based on the context passed to the view. When the selection changes, I want to pass the new data to Django through a POST request and create a pie chart, save it as an image and pass it back to the view.

I currently do this prior to the template loading, but when I try to pass the image in to context after the POST, I get nothing.

Charts in view.html

cmp_counts = []
    cmp_counts.append(len(Campaign.objects.filter(reviewComplete=False)))
    cmp_counts.append(len(Campaign.objects.filter(reviewComplete=True)))
    cmp_counts.append(len(Campaign.objects.filter(closed=True)))
    labels = ["Open", "Completed", "Closed"]
    for count in cmp_counts:
        if count == 0:
            index = cmp_counts.index(count)
            labels.remove(labels[index])
            cmp_counts.remove(count)
    print(cmp_counts)

    def auto(values):
        a = np.round(values/100*sum(cmp_counts), 0)

    plt.pie(cmp_counts, labels=labels, textprops={'color':"w"}, autopct=lambda x: '{:.0f}'.format(x*sum(cmp_counts)/100))
    plt.title('All Campaigns', color="w")
    
    tmpFile = BytesIO()
    plt.savefig(tmpFile, transparent=True, format='png')
    encoded = base64.b64encode(tmpFile.getvalue()).decode('utf-8')

    html = '<img class="report-graphs" src=\'data:image/png;base64,{}\'>'.format(encoded)

    context['pie_chart'] = html

    plt.clf()

This works just fine and creates the following pie chart: Pie Chart

However, when trying to pass the charts that should change based on selection, I don't receive any error, I just can't access the context.

HTML Script

<script>
    function update() {

        dict = {}

        stats = {}
        filter_stats = {}

        stats['comp'] = document.getElementById('comp_count').innerHTML.replace(/[^0-9]/g,'');
        stats['inc'] = document.getElementById('inc_count').innerHTML.replace(/[^0-9]/g,'');

        stats['appr_accts'] = document.getElementById('appr_count').innerHTML.replace(/[^0-9]/g,'');
        stats['den_accts'] = document.getElementById('den_count').innerHTML.replace(/[^0-9]/g,'');
        stats['inc_accts'] = document.getElementById('incRev_count').innerHTML.replace(/[^0-9]/g,'');
        

        filter_stats['comp'] = document.getElementById('filter_comp_count').innerHTML.replace(/[^0-9]/g,'');
        filter_stats['inc'] = document.getElementById('filter_inc_count').innerHTML.replace(/[^0-9]/g,'');

        filter_stats['appr_accts'] = document.getElementById('filter_appr_count').innerHTML.replace(/[^0-9]/g,'');
        filter_stats['den_accts'] = document.getElementById('filter_den_count').innerHTML.replace(/[^0-9]/g,'');
        filter_stats['inc_accts'] = document.getElementById('filter_incRev_count').innerHTML.replace(/[^0-9]/g,'');


        dict['stats'] = stats
        dict['filter_stats'] = filter_stats

        
        $.ajax({
            url: '/reports/',
            type: 'POST',
            headers: {'X-CSRFtoken': '{{ csrf_token }}'},
            data: dict,
            dataType: 'json'
        })
    }
</script>

This function is called on document load and is supposed to send the data back to Django to formulate the new charts

if request.method == "POST":
        data = request.POST
        
        stats = {}
        filter_stats = {}
        for item in data:
            if "stats" in item and "_stats" not in item:
                stats[item] = int(data[item])

            elif "_stats" in item:
                filter_stats[item] = int(data[item])

        print(stats)
        print(filter_stats)
        stats_rev_counts = []
        stats_rev_counts.append(stats['stats[comp]'])
        stats_rev_counts.append(stats['stats[inc]'])
        labels = ["Completed", "Incomplete"]
        for count in stats_rev_counts:
            if count == 0:
                index = stats_rev_counts.index(count)
                labels.remove(labels[index])
                stats_rev_counts.remove(count)

        plt.pie(stats_rev_counts, labels=labels, textprops={'color':"w"}, autopct=lambda x: '{:.0f}'.format(x*sum(stats_rev_counts)/100))
        plt.title('Reviewers', color="w")
        tmpFile = BytesIO()
        plt.savefig(tmpFile, transparent=True, format='png')
        encoded = base64.b64encode(tmpFile.getvalue()).decode('utf-8')
        html = '<img class="report-graphs" src=\'data:image/png;base64,{}\'>'.format(encoded)
        context['stats_revs'] = html

        plt.clf()

        stats_acct_counts = []
        stats_acct_counts.append(stats['stats[appr_accts]'])
        stats_acct_counts.append(stats['stats[den_accts]'])
        stats_acct_counts.append(stats['stats[inc_accts]'])
        labels = ["Approved", "Denied", "Incomplete"]
        for count in stats_acct_counts:
            if count == 0:
                index = stats_acct_counts.index(count)
                labels.remove(labels[index])
                stats_acct_counts.remove(count)

        plt.pie(stats_acct_counts, labels=labels, textprops={'color':"w"}, autopct=lambda x: '{:.0f}'.format(x*sum(stats_acct_counts)/100))
        plt.title('Accounts', color="w")
        tmpFile = BytesIO()
        plt.savefig(tmpFile, transparent=True, format='png')
        encoded = base64.b64encode(tmpFile.getvalue()).decode('utf-8')
        html = '<img class="report-graphs" src=\'data:image/png;base64,{}\'>'.format(encoded)
        context['stats_accts'] = html

        plt.clf()

        filter_rev_counts = []
        filter_rev_counts.append(filter_stats['filter_stats[comp]'])
        filter_rev_counts.append(filter_stats['filter_stats[inc]'])
        labels = ["Completed", "Incomplete"]
        for count in filter_rev_counts:
            if count == 0:
                index = filter_rev_counts.index(count)
                labels.remove(labels[index])
                filter_rev_counts.remove(count)

        plt.pie(filter_rev_counts, labels=labels, textprops={'color':"w"}, autopct=lambda x: '{:.0f}'.format(x*sum(filter_rev_counts)/100))
        plt.title('Reviewers', color="w")
        tmpFile = BytesIO()
        plt.savefig(tmpFile, transparent=True, format='png')
        encoded = base64.b64encode(tmpFile.getvalue()).decode('utf-8')
        html = '<img class="report-graphs" src=\'data:image/png;base64,{}\'>'.format(encoded)
        context['filter_revs'] = html

        plt.clf()

        filter_acct_counts = []
        filter_acct_counts.append(filter_stats['filter_stats[appr_accts]'])
        filter_acct_counts.append(filter_stats['filter_stats[den_accts]'])
        filter_acct_counts.append(filter_stats['filter_stats[inc_accts]'])
        labels = ["Approved", "Denied", "Incomplete"]
        for count in filter_acct_counts:
            if count == 0:
                index = filter_acct_counts.index(count)
                labels.remove(labels[index])
                filter_acct_counts.remove(count)

        plt.pie(filter_acct_counts, labels=labels, textprops={'color':"w"}, autopct=lambda x: '{:.0f}'.format(x*sum(filter_acct_counts)/100))
        plt.title('Accounts', color="w")
        tmpFile = BytesIO()
        plt.savefig(tmpFile, transparent=True, format='png')
        encoded = base64.b64encode(tmpFile.getvalue()).decode('utf-8')
        html = '<img class="report-graphs" src=\'data:image/png;base64,{}\'>'.format(encoded)
        context['filter_accts'] = html

        plt.clf()

        print(context)
        return render(request, 'AccessReview/report.html', context)

I know that this works and the images are created because the "print(context)" in the view shows all the images (I've truncated the encoded string values):

{'parent_template': 'AccessReview/adminbase.html', 'pie_chart': '<img class="report-graphs" src=\'data:image/png;base64,iVBO.....'>', 'bar_chart': '<img class="report-graphs" src=\'data:image/png;base64,iVBO....\'>', 'campaigns': <QuerySet [<Campaign: 1 2022-10-30>, <Campaign: 2 2022-12-31>, <Campaign: 3 2023-03-31>, <Campaign: 17 2022-11-30>, <Campaign: 18 2022-12-31>, <Campaign: 19 2022-09-13>]>, 'systems': <QuerySet [<System: ISE>]>, 'stats_revs': '<img class="report-graphs" src=\'data:image/png;base64,iVBO....'>', 'stats_accts': '<img class="report-graphs" src=\'data:image/png;base64,iVBO....'>', 'filter_revs': '<img class="report-graphs" src=\'data:image/png;base64,iVBO....'>', 'filter_accts': '<img class="report-graphs" src=\'data:image/png;base64,iVBO....'>'}

But when I try to show these images using {{stats_revs|safe}} nothing is shown. I have printed the context variables in the template as well and they are not being populated from the POST request. I'm at a loss at this point and want to know how I can generate these charts based on the current filter selection by the end user.

1 Answers

I can see that you are using AJAX to send POST data. Ajax sends the data to the view but does not render a new page. On the backend the data is modified, but without a refresh you won't be able to see the data. If you don't mind refreshing the page in your javascript method, you can do the following:

setTimeout(function(){
   window.location.reload();
}, 500);

The 500 being the number of miliseconds you want to take the page to reload after submitting the data.

Related