Important Update
The Guide Feature will be discontinued after December 15th, 2023. Until then, you can continue to access and refer to the existing guides.
Author avatar

Gaurav Singhal

How to Work with AJAX in Django

Gaurav Singhal

  • Dec 19, 2019
  • 19 Min read
  • 260,241 Views
  • Dec 19, 2019
  • 19 Min read
  • 260,241 Views
Languages Frameworks and Tools
Django

Introduction

AJAX stands for Asynchronous JavaScript And XML, which allows web pages to update asynchronously by exchanging data to and from the server. This means you can update parts of a web page without reloading the complete web page. It involves a combination of a browser built-in XMLHttpRequest object, JavaScript, and HTML DOM.

How AJAX Works

  1. An event occurs on a web page, such as an initial page load, form submission, link or button click, etc.
  2. An XMLHttpRequest object is created and sends the request to the server .
  3. The server responds to the request.
  4. The response is captured and then server respond back with response data.

There are many scenarios where you may want to make GET and POST requests to load and post data from the server asynchronously, back and forth. Additionally, this enables web applications to be more dynamic and reduces page load time.

Initial Setup

For this guide, we will use the jQuery library to easily implement JavaScript; moreover, we'll also use Bootstrap 4 to make the application look good.

Below is the base.html template, which includes the jQuery library and bootstrap framework. Make sure you include this link and script correctly. Also, note the content and javascript blocks, which are used later in this guide.

base.html

1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5    <meta charset="utf-8">
6    <meta http-equiv="X-UA-Compatible" content="IE=edge">
7    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8    <title>{% block title %}{% endblock title %}</title>
9    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
10    {% block style %}
11    {% endblock style %}
12</head>
13
14<body>
15    {% block content %}
16    {% endblock content %}
17
18    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
19    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
20    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
21    {% block javascript %}
22    {% endblock javascript %}
23</body>
24
25</html>
django

I am assuming that you already know how to do Django setup. If not, or if you are new to Django, please follow the Django documentation for initial setup.

Also, note that I have used Django version 2.2 for this guide.

Jump to Code

To make the guide more interactive, we will use a real-time example to demonstrate the POST and GET AJAX requests in Django.

We'll use a ScrapBook scenario in which a user can create a friend and the app will show it dynamically. It will also check if the nickname is already taken or not by sending a GET request to the server.

To get excited about what we're building, check out this screenshot. At the end of this guide, you'll be able to build the following app.

screenshot

Let's start by creating a Django app called "my_app" with the startapp command. Be sure to run the following manage.py commands where your manage.py lives, i.e., in your project folder.

1$ python manage.py startapp my_app
shell

After creating the Django app, make sure you add it in INSTALLED_APPS in settings.py .

settings.py

1INSTALLED_APPS += [
2    'my_app',
3]
python

Creating Models

Let's create an example model for a Friend with a minimal number of attributes.

1from django.db import models
2
3# Create your models here.
4class Friend(models.Model):
5    # NICK NAME should be unique
6    nick_name = models.CharField(max_length=100, unique =  True)
7    first_name = models.CharField(max_length=100)
8    last_name = models.CharField(max_length=100)
9    likes = models.CharField(max_length = 250)
10    dob = models.DateField(auto_now=False, auto_now_add=False)
11    lives_in = models.CharField(max_length=150, null = True, blank = True)
12
13    def __str__(self):
14        return self.nick_name
python

After creating the models, perform makemigrations and migrate by running the following commands.

1$ python manage.py makemigrations
2$ python manage.py migrate
shell

Then, run the Django server.

1$ python manage.py runserver
shell

POST Request

To submit the form, we need to make a POST request to the server with all the form values filled by the user.

1. Creating Forms

Let's create the Django form by inheriting ModelForm. In the FriendForm, I have changed the dob field and enabled a widget of DateField with some changes in years. And also note that in __init__ method, I have updated an HTML class attribute with form-control to every field of the form so that Bootstrap gets enabled on every field.

Finally, in the Meta subclass, I have included the modal class and fields that are likely to be displayed.

Note that I have created a new file called forms.py in my app folder.

forms.py

1from .models import Friend
2from django import forms
3import datetime
4
5class FriendForm(forms.ModelForm):
6    ## change the widget of the date field.
7    dob = forms.DateField(
8        label='What is your birth date?', 
9        # change the range of the years from 1980 to currentYear - 5
10        widget=forms.SelectDateWidget(years=range(1980, datetime.date.today().year-5))
11    )
12    
13    def __init__(self, *args, **kwargs):
14        super(FriendForm, self).__init__(*args, **kwargs)
15        ## add a "form-control" class to each form input
16        ## for enabling bootstrap
17        for name in self.fields.keys():
18            self.fields[name].widget.attrs.update({
19                'class': 'form-control',
20            })
21
22    class Meta:
23        model = Friend
24        fields = ("__all__")
python

2. Creating Views

After creating the form, let's import FriendForm in the views. There are two views that need to be discussed in this section, and they are indexView and postFriend.

  • indexView creates the FriendForm object, takes all the friends objects from the database, and sends them to the index.html template, which we will discuss later.
  • postFriend is AJAX POST view, which handles the POST request. You will notice that it is similar to a regular view, but with some changes, such as JsonResponse and serialize. We have used these methods because it is an AJAX view, so we need to deal with JSON only.

views.py

1from django.http import JsonResponse
2from django.core import serializers
3from .forms import FriendForm
4from .models import Friend
5
6def indexView(request):
7    form = FriendForm()
8    friends = Friend.objects.all()
9    return render(request, "index.html", {"form": form, "friends": friends})
10
11def postFriend(request):
12    # request should be ajax and method should be POST.
13    if request.is_ajax and request.method == "POST":
14        # get the form data
15        form = FriendForm(request.POST)
16        # save the data and after fetch the object in instance
17        if form.is_valid():
18            instance = form.save()
19            # serialize in new friend object in json
20            ser_instance = serializers.serialize('json', [ instance, ])
21            # send to client side.
22            return JsonResponse({"instance": ser_instance}, status=200)
23        else:
24            # some form errors occured.
25            return JsonResponse({"error": form.errors}, status=400)
26
27    # some error occured
28    return JsonResponse({"error": ""}, status=400)
python

3. Creating URLs

For the above views, let's create a URL path for each view. Note the name given to the postFriend path, which will be used in the template discussed later in this guide.

urls.py

1from django.urls import path
2from my_app.views import (
3    indexView,
4    postFriend, 
5)
6
7urlpatterns = [
8    # ... other urls
9    path('', indexView),
10    path('post/ajax/friend', postFriend, name = "post_friend"),
11    # ...
12]
python

4. Creating Templates

Now that you've created the backend, let's move to the frontend part of this guide.

In the index.html, we will first extend our base.html, which is being discussed earlier in this guide. Moreover, will write the content between the blocks.

The template is divided into two parts. The first part renders the form, and the second displays the previous stored friends objects in the table.

index.html

1{% extends "base.html" %}
2
3{% block content %}
4
5<div class="container-fluid">
6    <form id="friend-form">
7        <div class="row">
8            {% csrf_token %}
9            {% for field in form %}
10            <div class="form-group col-4">
11                <label class="col-12">{{ field.label }}</label>
12                {{ field }}
13            </div>
14            {% endfor %}
15            <input type="submit" class="btn btn-primary" value="Create Friend" />
16        </div>
17    <form>
18</div>
19<hr />
20
21<div class="container-fluid">
22    <table class="table table-striped table-sm" id="my_friends">
23        <thead>
24            <tr>
25                <th>Nick name</th>
26                <th>First name</th>
27                <th>Last name</th>
28                <th>Likes</th>
29                <th>DOB</th>
30                <th>lives in</th>
31            </tr>
32        </thead>
33        <tbody>
34        {% for friend in friends %}
35        <tr>
36            <td>{{friend.nick_name}}</td>
37            <td>{{friend.first_name}}</td>
38            <td>{{friend.last_name}}</td>
39            <td>{{friend.likes}}</td>
40            <td>{{friend.dob | date:"Y-m-d"}}</td>
41            <td>{{friend.lives_in}}</td>
42        </tr>
43        {% endfor %}
44        </tbody>
45    </table>
46
47</div>
48{% endblock content %}
django

Now let's move to the JavaScript part of this guide.

On submitting the form, serialize the form data and create an AJAX POST request, then send it to the server.

On successful request, append the row to the table.

Note we have used the revered URL, which is discussed in the urls.py section.This helps you not to write the URL path in a hardcoded way.

You can put this reverse URL tag in the HTML attribute and then fetch the attribute afterwards. So put this JavaScript code in the js file.

index.html

1{% block javascript %}
2<script>
3    /*
4        On submiting the form, send the POST ajax
5        request to server and after successfull submission
6        display the object.
7    */
8    $("#friend-form").submit(function (e) {
9        // preventing from page reload and default actions
10        e.preventDefault();
11        // serialize the data for sending the form data.
12        var serializedData = $(this).serialize();
13        // make POST ajax call
14        $.ajax({
15            type: 'POST',
16            url: "{% url 'post_friend' %}",
17            data: serializedData,
18            success: function (response) {
19                // on successfull creating object
20                // 1. clear the form.
21                $("#friend-form").trigger('reset');
22                // 2. focus to nickname input 
23                $("#id_nick_name").focus();
24
25                // display the newly friend to table.
26                var instance = JSON.parse(response["instance"]);
27                var fields = instance[0]["fields"];
28                $("#my_friends tbody").prepend(
29                    `<tr>
30                    <td>${fields["nick_name"]||""}</td>
31                    <td>${fields["first_name"]||""}</td>
32                    <td>${fields["last_name"]||""}</td>
33                    <td>${fields["likes"]||""}</td>
34                    <td>${fields["dob"]||""}</td>
35                    <td>${fields["lives_in"]||""}</td>
36                    </tr>`
37                )
38            },
39            error: function (response) {
40                // alert the error if any error occured
41                alert(response["responseJSON"]["error"]);
42            }
43        })
44    })
45</script>
46{% endblock javascript %}
django

GET Request

Now let's move on to the GET request. In our current scenario, before submitting the form, we can check if a nickname already exists in the database or not by sending the currently entered nickname back to the server.

Following is the screenshot of what we are to going to build in this section.

screenshot

1. Creating Views

Let's create the view for the following scenario. In the checkNickName view, we first take the nickname which has been sent by the AJAX request and then check whether any friend has this nickname in the database. If it already exists, then we return with valid as False, else True.

1from django.http import JsonResponse
2from .models import Friend
3
4def checkNickName(request):
5    # request should be ajax and method should be GET.
6    if request.is_ajax and request.method == "GET":
7        # get the nick name from the client side.
8        nick_name = request.GET.get("nick_name", None)
9        # check for the nick name in the database.
10        if Friend.objects.filter(nick_name = nick_name).exists():
11            # if nick_name found return not valid new friend
12            return JsonResponse({"valid":False}, status = 200)
13        else:
14            # if nick_name not found, then user can create a new friend.
15            return JsonResponse({"valid":True}, status = 200)
16
17    return JsonResponse({}, status = 400)
python

2. Creating URLs

For the above view, let's create a URL route path named validate_nickname.

1from django.urls import path
2from my_app.views import (
3    checkNickName
4)
5
6urlpatterns = [
7    # ...other urls
8    path('get/ajax/validate/nickname', checkNickName, name = "validate_nickname")
9    # ...
10]
python

3. Creating Templates

Now let's write the AJAX GET request on the focusout event on the nick_name input by grabbing the current nick_name value and sending it to the server.

After a successful GET request, notify whether the nick_name is taken or not.

1{% block javascript %}
2<script>
3    /*
4    On focus out on input nickname,
5    call AJAX get request to check if the nickName
6    already exists or not.
7    */
8    $("#id_nick_name").focusout(function (e) {
9        e.preventDefault();
10        // get the nickname
11        var nick_name = $(this).val();
12        // GET AJAX request
13        $.ajax({
14            type: 'GET',
15            url: "{% url 'validate_nickname' %}",
16            data: {"nick_name": nick_name},
17            success: function (response) {
18                // if not valid user, alert the user
19                if(!response["valid"]){
20                    alert("You cannot create a friend with same nick name");
21                    var nickName = $("#id_nick_name");
22                    nickName.val("")
23                    nickName.focus()
24                }
25            },
26            error: function (response) {
27                console.log(response)
28            }
29        })
30    })
31</script>
32{% endblock javascript %}
django

BONUS: Using Class Bases Views

If you have some experience with Django, then you probably know that you can create views by function and by Class. Most developers get confused about which to use and when. So in this short guide, let's convert the above FBV code to CBV code.

For this guide, I have combined the indexView and postFriend functions into a single class called FriendView, which inherits the View class and has two methods called get and post, respectively.

views.py

1from django.shortcuts import render
2from django.http import JsonResponse
3from django.core import serializers
4from .forms import FriendForm
5from .models import Friend
6
7from django.views import View
8
9class FriendView(View):
10    form_class = FriendForm
11    template_name = "index.html"
12
13    def get(self, *args, **kwargs):
14        form = self.form_class()
15        friends = Friend.objects.all()
16        return render(self.request, self.template_name, 
17            {"form": form, "friends": friends})
18
19    def post(self, *args, **kwargs):
20        if self.request.is_ajax and self.request.method == "POST":
21            form = self.form_class(self.request.POST)
22            if form.is_valid():
23                instance = form.save()
24                ser_instance = serializers.serialize('json', [ instance, ])
25                # send to client side.
26                return JsonResponse({"instance": ser_instance}, status=200)
27            else:
28                return JsonResponse({"error": form.errors}, status=400)
29
30        return JsonResponse({"error": ""}, status=400)
python

urls.py

Let's write the urlpattern for the above discussed CBV.

1from django.urls import path
2from my_app.views import (
3    FriendView
4)
5
6urlpatterns = [
7    # ... other urls
8    path("", FriendView.as_view(), name = "friend_cbv"),
9    # ...
10]
python

To transform from FBV to CBV, you need to change the reverse URL pattern.

index.js

1// other previous stuff
2$.ajax({
3    type: 'POST',
4    url: "{% url 'friend_cbv' %}", // CHANGE the POST url
5    // ... continues
6// ...
js

Conclusion

AJAX is the best way to perform asynchronous tasks in Django, at least on a small scale. If you want to do an asynchronous task on a bigger scale, you could do socket programming in Django or use front-end JavaScript libraries such as Angular, Vue, or React.

If you have any difficulty following this guide, you can refer to this Github Repository or the other resources below. If you still have any queries, feel free to contact me at CodeAlphabet.

Learn More

Explore these Django and JavaScript courses from Pluralsight to continue learning: