How to integrate AJAX With Django applications

Overview

Ajax or Asynchronous JavaScript And XML is an approach or process that allows the frontend to make asynchronous calls to the backend server without having to reload the entire webpage.

In other words, it helps make a website more dynamic where parts of a webpage can update without affecting the state of the other elements, by posting or getting data from the server.

We can use Ajax calls in a Django project to create more dynamic templates since Django as a standalone web framework does not have a way to make asynchronous communication with its views and templates.

In this guide, we’ll use a simple example to show how Ajax calls can be implemented in Django. To do so, we can create an endpoint that allows a user to cast a vote for a choice in a poll.

Step 1: Create the models

To create a poll, where a user can cast a vote, two separate models are required.

  • The Question model contains the question text and information related to its publishing date. One Question object will have multiple choices for a person to choose from
  • The Choice model belongs to a single question object. Each Choice object will contain the choice text and the number of votes.
from django.db import models
# Create your models here.
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)

Step 2: Create the views

In this example, the frontend only has a page to display all the questions and a form that allows a user to cast a vote for each poll.

To do this, we need the index and vote views.

from django.shortcuts import render, HttpResponseRedirect
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
# Create your views here.
from .models import Question, Choice
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# Redisplay the question voting form.
return JsonResponse({
'question': question.question_text,
'error_message': "You didn't select a choice.",
})
selected_choice.votes += 1
selected_choice.save()
response_data = {}
response_data["status"] = "Success"
response_data["choices"] = list(question.choice_set.order_by('-votes').values())
# Notice how the view returns a json object instead of redirecting
return JsonResponse(response_data)

Explanation

  • Line 9–12: The index view is responsible for rendering a simple page that has all the Question objects available to it in its context to display. We don’t need Ajax calls here.

  • Line 14–33: The vote view returns a JSON response object instead of rendering a different HTML page. This is the view that will be receiving the AJAX calls from the frontend template.

  • Line 15: We locate the question for which a vote was cast.

  • Line 17–24: We deal with any situations when the choice is not available in the database.

  • Line 26–31: We deal with incrementing the vote for the selected option and then encapsulating the updated tally in a JSON object to be returned back to the template.

Step 3: Create the HTML template

Next, we need to allow the user to cast a vote. We can do that in the index.html template file.

<script src='https://cdnjs.cloudflare.com/ajax/libs/axios/0.9.1/axios.js'></script>
<script>
function castVote(formObj) {
event.preventDefault()
axiosSetup(formObj)
}
function axiosSetup(formObj) {
// for geting the input value to pass at server for collecting there value and save in db
let data = new FormData()
selectedChoice = formObj.querySelector('input[name="choice"]:checked');
if (selectedChoice != null) {
selectedChoice.checked = false;
questionID = selectedChoice.parentElement.id
selectedChoice = selectedChoice == null ? -1 : selectedChoice.value
data.append("choice", selectedChoice);
data.append('csrfmiddlewaretoken', '{{csrf_token}}') // setup csrf_token as a post request
// ....axios post request
let url = "{% url 'polls:vote' question_id=12345 %}".replace(/12345/, questionID);
axios.post(url, data)
.then(res => {
document.getElementById("vote-form"+questionID).style.display = "none"
document.getElementById("vote-result"+questionID).style.display = "block"
displayVotingResult(res.data.choices, questionID)
})
.catch(e => {
location.href="/polls/{{question.id}}/results"
})
}
}
function displayVotingResult(questionChoices, questionID) {
let resultList = document.getElementById("result-list"+questionID);
for(let i = 0; i < questionChoices.length; i++) {
entry = document.createElement('li');
entry.classList.add("list-group-item");
entry.appendChild(document.createTextNode(questionChoices[i].choice_text + ' -- ' + questionChoices[i].votes ));
resultList.appendChild(entry);
}
}
</script>

This part can be broken down into two separate components. However, the entire code can be a part of the same index.html file.

The HTML form

The HTML portion of the template is a plain form. By injecting Django into the template, we render each poll iteratively as a separate form. It is important to note that the id for each dynamic form is unique. This is done using the question object’s id.

id="vote-form{{question.id}}"

This is done so that the javaScript function can identify which question the submit request is made for.

The form has to be submitted using ajax calls, the <form> tag will have a JavaScript function bound to it rather than providing it with a django URL. Here, the form data would have been posted if the ajax call were not being made.

onsubmit="return castVote(this)" 

The Ajax call

In order to make AJAX calls, we have to import the JavaScript library:

<script src='https://cdnjs.cloudflare.com/ajax/libs/axios/0.9.1/axios.js'></script>

The axiosSetup function is responsible for making the actual asynchronous call to the server.

Explanation

  • Line 11: We initialize a formObj object, which is sent as a part of the POST request.

  • Line 13: We locate the option that was selected in the form. The selection option is a part of a radio button input.

  • Line 16–20: We verify that the selected option is a valid value.

  • Line 22–23: We append the csfr token to the form data. This is necessary for the security of the application.

  • Line 27: We prepare the URL for the POST request. In this case, this will be the vote view that was defined earlier. The URL has the question ID against which an option was selected and also appended to it. A placeholder value is inserted initially in the string when creating a Django url, and the question ID is injected in from JavaScript.

  • Line 28–38: We make an AJAX call using the Axios library. The call awaits the response from the backend. After that, the then blockis executed. In case of an error, it executes the catch instead.

Live Example

Press the “Run” button below to execute the application with the voting example:

Explanation

The index.html file contains both the HTML form and the JavaScript functions.

  • Line 1: We use the index.html file to check if there is at least one question object present in the latest_question_list. This is done using the if condition using Django’s HTML template programming construct. If the condition is met, the respective code block with the forms is executed. Otherwise, the else statement block executes at line 28.

  • Line 3: This is where the loop iterating over each question object begins.

  • Line 5: The <form> tag is the same for each question as only the ID of the question and the ID of the selected option for that question is passed on to the view on the backend.

  • Line 10–15: For each question, each of its valid choices is iterated over and printed as a radio option.

  • Line 32: We use the <script> tag to import the axios package. For different cases, this import tag can be moved to a base HTML file to allow the Ajax calls to be used in more than one file.

When the input button is pressed, the castVote function gets called. This function does two things.

  1. Calls the axiosSetup function for the ajax call.
  2. The preventDefault method stops the form from redirecting or enabling any other default behavior on the form submission.
  • Line 40: The axiosSetup function prepares the ajax call to be made.

  • Line 61–63: This block only executes if the ajax call executes properly. On successfully casting the vote on the backend, the frontend will use CSS styling to hide the options view from the form for the question for which the vote was cast. Instead, the HTML will display the total count of votes cast for each option in that question. This is done through the displayVotingResult option defined on line 72.

The displayVotingResult function will retrieve the div with the result-list id unique for each question. This div is then iteratively injected with a list item HTML statement to display the option and its vote count. Uniqueness in the vote-results divs and the result-list{{question.id}} unordered list is preserved using the same approach for the <form> tag, i.e, using the question object’s id itself.

Note: You can make changes to the code and rerun the application using the same “Run” button to view the changes live.

Free Resources