Django+Ajax CRUD

In this tutorial, you'll learn how to send Ajax requests in Django 2 and Python 3.7 to add CRUD operations in your application and manipulate your Django models and database without having to refresh your web pages each time.

Ajax stands for Asynchronous JavaScript and XML and it's a way for getting data from the server and updating the page on the fly without refreshing the page.

Creating a Virtual Environment

Make sure you have Python 3 installed (Python 3.7 is the latest as of this writing) and start by creating a virtual environment for your project's packages:

$ python -m venv myenv

Next, activate your virtual environment using:

$ source myenv/bin/activate

 

 

Installing Django 2 and Creating a Project

Now, you need to install Django using pip:

$ python -m pip install django

Next, create a Django project using:

$ django-admin startproject djangoajaxdemo

Next you need to create a Django application using the manage.py script:

$ cd djangoajaxdemo
$ python manage.py startapp rooms

Next you need to add it to your project's installed apps array in the settings.py file:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rooms'
]

Adding jQuery

In this tutorial, we'll be using jQuery to send Ajax requests to Django. You can also use any other HTTP client like Axios, the JavaScript Fetch API available on modern browsers or the XMLHttpRequest interface.

First of all, you need to get jquery from the official website and include it in your project or use a CDN. Go to the official website and get the CDN of the version of jQuery you want to use.

In my case, I'll be using jQuery 3.3.1 from https://code.jquery.com/jquery-3.3.1.min.js.

Inside the rooms application, create a templates/rooms folder and create a base.html file:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title> Django Ajax CRUD with jQuery</title>
<link  rel="stylesheet"  href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"   crossorigin="anonymous">
</head>
  <body>
    <div class="container d-flex h-100">
      <div class="row justify-content-center">
        <div class="col-10">
          {% block main %}
          {% endblock %}
        </div>
      </div>
    </div>
{% block js %}
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
{% endblock %}
{% block extrajs %}
{% endblock %}
  </body>
</html>

Adding a Model

We'll be adding CRUD operations against a Room model. Open the rooms/models.py file and add the following code:

from django.db import models



class  Room(models.Model):
    ROOM_TYPES = (
        (1, 'Single'),
        (2, 'Double'),
        (3, 'Triple'),
    )

    name = models.CharField(max_length=50)
    status = models.CharField(max_length=30, blank=True)
    room_number = models.IntegerField(blank=True, null=True)
    nobeds = models.IntegerField(blank=True, null=True)
    room_type = models.PositiveSmallIntegerField(choices=ROOM_TYPES)

Adding CRUD Views

In the rooms/views.py file add the following class based and generic views for performing CRUD operations:

from django.views.generic import View
from django.http import JsonResponse
from django import forms
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.forms.models import model_to_dict
from .models import Room

class  RoomForm(forms.ModelForm):
    class  Meta:
        model = Room
        fields =  '__all__'

class  RoomList(View):
    def  get(self, request):
        rooms =  list(Room.objects.all().values())
        data =  dict()
        data['rooms'] = rooms
        return JsonResponse(data)

class  RoomDetail(View):
    def  get(self, request, pk):
        room = get_object_or_404(Room, pk=pk)
        data =  dict()
        data['room'] = model_to_dict(room)
        return JsonResponse(data)

@method_decorator(csrf_exempt, name='dispatch')
class  RoomCreate(CreateView):
    def  post(self, request):
        data =  dict()
        form = RoomForm(request.POST)
        if form.is_valid():
            room = form.save()
            data['room'] = model_to_dict(room)
        else:
            data['error'] =  "form not valid!"
        return JsonResponse(data)

class  RoomUpdate(View):
    def  post(self, request, pk):
        data =  dict()
        room = Room.objects.get(pk=pk)
        form = RoomForm(instance=room, data=request.POST)
        if form.is_valid():
            room = form.save()
            data['room'] = model_to_dict(room)
        else:
            data['error'] =  "form not valid!"
        return JsonResponse(data)

class  RoomDelete(View):
    def  post(self, request, pk):
        data =  dict()
        room = Room.objects.get(pk=pk)
        if room:
            room.delete()
            data['message'] =  "Room deleted!"
        else:
            data['message'] =  "Error!"
        return JsonResponse(data)

Next, let's add the urls. Open the urls.py file and add:

from django.urls import path, include
from django.views.generic.base import TemplateView
from rooms import views

urlpatterns = [
    path('rooms/', TemplateView.as_view(template_name="rooms/main.html"), name='room_main'),
    path('rooms/list', views.RoomList.as_view(), name='room_list'),
    path('rooms/create', views.RoomCreate.as_view(), name='room_create'),
    path('rooms/update/<int:pk>', views.RoomUpdate.as_view(), name='room_update'),
    path('rooms/delete/<int:pk>', views.RoomDelete.as_view(), name='room_delete'),
    path('rooms/<int:pk>', views.RoomDetail.as_view(), name='room_detail'), 
]

Adding a Template

Since we'll be using Ajax for making CRUD operations in our Django application we will not need multiple pages or templates but instead we'll conceive our application as a Single Page Application.

Let's create a main.html file inside the rooms/templates/rooms folder with the following content:

{% extends 'rooms/base.html' %}
{% block main %}  
{% endblock %}

{% block extrajs %}
<script  src="{% static 'js/app.js' %}"></script>
{% endblock %}

Next under your project's root folder create the static/js/ folder and add an app.js file with the following content:

$(function () {
    console.log("Hello!");
});

In your urls.py file, add the following url pattern so you can access your static files in development mode:

from django.conf import settings
# [...]

if settings.DEBUG:
    from django.contrib.staticfiles.urls import staticfiles_urlpatterns
    urlpatterns += staticfiles_urlpatterns()

In the settings.py file, add the following setting to configure your static folder:

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

If your run your application and visit the http://127.0.0.1:8000/rooms/ you should see a Hello! in the console of your browser which means you static files are configured correctly.

In this tutorial, we'll implement the list and delete operations. For create and update operations we'll see them in the next tutorial.

Getting Rooms with jQuery.ajax()

In your app.js file, add the following code to get data from the rooms/list endpoint by sending a GET Ajax request:

$.ajax({
    url:  '/rooms/list',
    type:  'get',
    dataType:  'json',
    success: function  (data) {
        let rows =  '';
        data.rooms.forEach(room => {
        rows += `
        <tr>
            <td>${room.room_number}</td>
            <td>${room.name}</td>
            <td>${room.nobeds}</td>
            <td>${room.room_type}</td>
            <td>
                <button class="btn deleteBtn" data-id="${room.id}">Delete</button>
                <button class="btn updateBtn" data-id="${room.id}">Update</button>
            </td>
        </tr>`;
    });
    $('[#myTable](https://paper.dropbox.com/?q=%23myTable) > tbody').append(rows);
    $('.deleteBtn').each((i, elm) => {
        $(elm).on("click",  (e) => {
            deleteRoom($(elm))
        })
    })
    }
});

Deleting Rooms with jQuery.ajax()

Next, you need to add an implementation for the deleteRoom(e) method:

function  deleteRoom(el){
    roomId  =  $(el).data('id')
    $.ajax({
        url:  `/rooms/delete/${roomId}`,
        type:  'post',
        dataType:  'json',
        success:  function (data) {
            $(el).parents()[1].remove()
        }
    });
}

Now go back to your rooms/templates/rooms/main.html template and add the table:

{% block main %}
<table  class="table table-bordered"  id="myTable">
<thead>
<th>
Room Number
</th>
<th>
Name
</th>
<th>
Number of Beds
</th>
<th>
Type
</th>
<th>
Actions
</th>
</thead>
<tbody>
</tbody>
</table>

<div  id="roomform">
    <button  id="createRoom"  class="btn"> Create Room </button>
</div>

{% endblock %}

This is a screenshot of our page at this point: