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.
To create a poll, where a user can cast a vote, two separate models are required.
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 fromChoice
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)
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, HttpResponseRedirectfrom django.http import HttpResponse, JsonResponsefrom django.shortcuts import render, get_object_or_404from django.urls import reverse# Create your views here.from .models import Question, Choicedef 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 += 1selected_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 redirectingreturn JsonResponse(response_data)
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.
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 dblet data = new FormData()selectedChoice = formObj.querySelector('input[name="choice"]:checked');if (selectedChoice != null) {selectedChoice.checked = false;questionID = selectedChoice.parentElement.idselectedChoice = selectedChoice == null ? -1 : selectedChoice.valuedata.append("choice", selectedChoice);data.append('csrfmiddlewaretoken', '{{csrf_token}}') // setup csrf_token as a post request// ....axios post requestlet 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 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)"
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.
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.
Press the “Run” button below to execute the application with the voting example:
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, thecastVote
function gets called. This function does two things.
- Calls the
axiosSetup
function for the ajax call.- 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 thediv
with theresult-list
id unique for each question. This div is then iteratively injected with alist item
HTML statement to display the option and its vote count. Uniqueness in thevote-results
divs and theresult-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.