Django admin : How to show or hide fields in change form based on another field value

April 2020

In this tutorial, we will learn how to customize a change form in the django admin to show or hide fields by adding some custom javascript.

Please notice this is an advanced tutorial, and you should be already comfortable with Django Admin.

You can find the source code in this repository

For this tutorial we will use the following models

# *coding: utf-8*
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
class Shop(models.Model):
    user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
    name = models.TextField()
    email = models.CharField(max_length=200)
    address = models.CharField(max_length=1024,null=True, blank=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    def __str__(self):
        return u'%s' % (self.name)
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 __str__(self):
        return u'%s - %s' % (self.refShop,self.title)

Nothing tricky. We define a Shop which can have Product. Now if we look at the Product change form in the admin, we will see

As we can see we have a checkbox WithEndDate which means some products can have an end date and others will not have.
So i will be nice to show the endDate and description fields in this interface, only if the checkbox withEndDate is checked.

This is what will learn to do in this tutorial.

First, we will need to override the default Django admin change form template to add our custom code in it. And we want to add our custom code only for the product models. So we need to create a backoffice/templates/admin/backoffice/product directory (where backoffice is the name of our django application) and then copy the default admin change_form.html file in our new directory.

cp ../../../environnements/genericenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_form.html backoffice/templates/admin/backoffice/product/

Please adjust the file path depending on where is located your virtualenvironnement

Ok now it’s time to add our code. As you can guess to be able to show or hide fields endDate and description we will need to use javascript and jQuery.
So first we will load the jquery javascript library:

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

inside our change_form.html and more precisely in the block of code:

{% block admin_change_form_document_ready %} 

located at the end of the html file.

Now we will add our javascript code which will check if the withEndDate checkbox is checked.
If not checked, we need to hide the fields endDate and description.
To do so, we get these elements in the DOM of the HTML and we hide them using the css instruction : display: none!important;
Otherwise we display them usign the css instruction display: block!important;

And of course each time the user clicks the withEndDate checkbox, we need to check if we show or hide the fields.

Here is the javacript code:

<script>
django.jQuery(document).ready(function(){
    if (django.jQuery('#id_withEndDate').is(':checked')) {
         hide_date=false;
    } else {
         var elementDate = document.getElementsByClassName("form-row field-endDate");
         var i;
         for (i = 0; i < elementDate.length; i++) {
            elementDate[i].style ="display: none!important";
         }
         var elementInformation = document.getElementsByClassName("form-row field-description");
         var i;
         for (i = 0; i < elementInformation.length; i++) {
            elementInformation[i].style ="display: none!important";
         }
        hide_date=true;
    }
    django.jQuery("#id_withEndDate").click(function(){
        hide_date=!hide_date;
        if (hide_date) {
            var elementDate = document.getElementsByClassName("form-row field-endDate");
             var i;
             for (i = 0; i < elementDate.length; i++) {
                elementDate[i].style ="display: none!important";
             }
             var elementInformation = document.getElementsByClassName("form-row field-description");
             var i;
             for (i = 0; i < elementInformation.length; i++) {
                elementInformation[i].style ="display: none!important";
             }
        } else {
             var elementDate = document.getElementsByClassName("form-row field-endDate");
             var i;
             for (i = 0; i < elementDate.length; i++) {
                elementDate[i].style ="display: block!important";
             }
             var elementInformation = document.getElementsByClassName("form-row field-description");
             var i;
             for (i = 0; i < elementInformation.length; i++) {
                elementInformation[i].style ="display: block!important";
             }
        }
    })
})
</script>

And here is the results:

You can find the source code in this repository

Christophe Surbier