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:

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.