How to make Ajax request in Django Admin changeview page ?

Product selected

April 2020

In this tutorial, we will learn how to create an Ajax request to validate some data while editing a value of a model in the Django Admin change_form.

You can find the source code in this repository

Build an Ajax request from scratch in Django Admin

Like other tutorials on this website, we will use the following model

class Product(models.Model):
    user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
    refShop = models.ForeignKey(Shop, db_index=True,on_delete=models.CASCADE)
    title = models.TextField(max_length=65)
    price = models.FloatField(default=0)
    withEndDate = models.BooleanField(default=False)
    endDate = models.DateField(blank=True,null=True)
    description = models.CharField(max_length=65, default='', null=True, blank=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    available = models.BooleanField(default=True)

    def clean(self):
        from datetime import datetime, timedelta
        from django.core.exceptions import ValidationError

        today = datetime.now().date()
        if self.endDate:
            if self.endDate < today:
                raise ValidationError("You cannot set an endDate in the past")

        if self.withEndDate and self.endDate is None:
            raise ValidationError("Please select an end date !")

    def __unicode__(self):
        return u'%s - %s' % (self.refShop,self.title)

Let’s say that when adding or editing a Product, we would like to verify that the price entered by the user in between a certain range of values. To do that, we will need to listen (using javascript) to the price input box, and if a value is entered, we will do an Ajax request to check if the value is within the range

Product selected

First we will need to override the default change_form.html file of the admin, by creating a new folder <application>/templates/admin/<application>/product/

and copy in it the change_form.html file

(from your virtualenv cp ../../env/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_form.html )

Before to modify this file, we will have a look at the source code of the admin page, to know how the price field is named

 <div class="form-row field-price">
    <div>
       <label class="required" for="id_price">Price:</label>
       <input type="number" name="price" value="2.0" step="any" required id="id_price">
    </div>
</div>

As we can expect the price is an input field identified by “id_price”. So we modify the change_form to add our javascript code inside the following block

{% block admin_change_form_document_ready %}
 <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
 <script>
django.jQuery(document).ready(function(){
    django.jQuery("#id_price").change(function(){
        console.log("Price change");
    });
})
</script>

Before implementing our Ajax request, we just log in the javascript console that the price has changed, to check everything is ok.

If everything is ok, we can now implement our Ajax call

<script>
django.jQuery(document).ready(function(){
    django.jQuery("#id_price").change(function(){
        console.log("Price change");
        var price = $(this).val();
        var url="http://127.0.0.1:8000/backoffice/checkPrice/"
        $.ajax({                       // initialize an AJAX request
        url: url,                    // set the url of the request
        data: {
          'price': price
        },
        success: function (data) {   // `data` is the return of the `checkPrice` view function
            console.log(data);
        }
      });
    });
})
</script>

We get the price using $(this).val(); and then we construct our url on which will we made a call. Now if we reload our page and try to change the price, we will see on the javascript console log

We have a 404 error page which is logic since we haven’t implemented yet this checkPrice view. So let’s do this.

In the views.py file of our application (backoffice), we create a simple view function to check our price value

from django.http import JsonResponse

def checkPrice(request):
    price = request.GET['price']
    if int(price) > 10:
       data = {"status": "KO"}
    else:
        data = {"status":"OK"}
    return JsonResponse(data)

In this view it’s up to you to implement your business code. For this demo, we will check if the price is greater than 10 and will send back a JSON with a KO or OK status

Then in our backoffice directory, we create an urls.py file

# coding=utf-8
from django.conf.urls import  url
from backoffice.views import *

urlpatterns = [
    url(r'^backoffice/checkPrice/$', checkPrice, name='checkPrice'),
]

[adsforwp id=”270″]

And finally we declare this new route url inside our project urls.py file

from django.conf.urls import url,include
from django.contrib import admin
from backoffice.admin import site
admin.site = site
admin.autodiscover()

urlpatterns = [
    url('', include('backoffice.urls')),
    url(r'^admin/', admin.site.urls),
]

Now if we reload our admin page, and try to change the price, we will see in our javascript console log, the Ajax answer with our status. It’s up to you to do whatever with the Ajax response. For demo, we will check if the status is KO and if so will set the price to 10 in the input box.

<script>
django.jQuery(document).ready(function(){
    django.jQuery("#id_price").change(function(){
        console.log("Price change");
        var price = $(this).val();
        var url="http://127.0.0.1:8000/backoffice/checkPrice/"
        $.ajax({                       // initialize an AJAX request
        url: url,                    // set the url of the request
        data: {
          'price': price
        },
        success: function (data) {   // `data` is the return of the `checkPrice` view function
            console.log(data);
            if (data["status"]=="KO"){
                $("input#id_price").val(10);
            }
        }
      });
    });
})
</script>

Et voilà ! now if you reload the admin page and try to enter a value greater than 10 for the price, the value will be set automatically to 10 thanks to our Ajax call.

Christophe Surbier