Django with Javascript

Dependent Select Boxes in Django with Javascript

Spread the love

Dependent Dropdown in Django with Ajax

Let’s say you have 2 dropdowns, and one is dependent on the other. We call the ‘dependent’ dropdown as ‘child’ and the other one as ‘parent’ in this article.

You want to show the corresponding child items in the child dropdown for each parent item in the parent dropdown, as you can see in the image:

Dependent dropdown list
List of states and cities dependent on it

 

In our example below, the parent dropdown is a Country and the child dropdown are the cities in the Country.

So lets dive right into it.

If you want to get started with Django Models, you can start here.

In our models.py we create 2 models as follows:

from django.db import models

# Create your models here.


class Country(models.Model):
    name = models.CharField(max_length=60,null=False)

    def __str__(self):
        return f'{self.name}'
class City(models.Model):
    name = models.CharField(max_length=60,null=False)
    country = models.ForeignKey(to="Country",on_delete=models.CASCADE,related_name="city_set")

    def __str__(self):
        return f'{self.name}'

The model ‘Country’ is our parent model here, while the model ‘City’ is the child model.

 Now we need two function based views. One to show all the items in the parent model (Country in our case) and other that filters the child model based on id (the foreign key) of the parent model.

 Here is what our views.py looks like:

from django.shortcuts import render
from .models import Country,City
import json
from django.http import JsonResponse
# Create your views here.


def index(request):
    countries = Country.objects.all()
    context = {
        'countries' : countries,
    }
    return render(request,'index.html',context=context)



def ajax_handler(request,id):
    cities = City.objects.filter(country__id=id).values_list('id','name')
    print(cities)
    cities = dict(cities)
    return JsonResponse({
        'cities' : cities,
    })


In the snippet above, the function ‘index’ just renders the ‘index.html’ with a dropdown containing all the countries. 

The function ‘ajax_handler’ takes in the id of the country that user selects from the dropdown, and returns the corresponding city names and ids as a JSON response. We send JSON back so that Javascript can parse it and update the child dropdown.

Here’s our ‘index.html’ :

{% load static %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Index</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="{% static 'css/index.css' %}">
    <script src='{% static "js/index.js" %}'></script>
</head>
<body>
    <h1>Just a basic template </h1>

    <form>
        <select name="country" class="country" id='country'>
                <option value="no_country">Select a country</option>
                {% for country in countries %}

                <option value="{{country.pk}}">{{country.name}}</option>
        
                {% endfor %}

        </select>
        
        <select name="city" id="city">

        </select>

    </form>
</body>
</html>

We are importing the js/index.js on line 10, we will have a look at it soon. In our index.html file, we have 2 select boxes, one of which is empty (this is the one for cities). The first select box has all the countries in our Country model.

Lets have a look at js/index/js now, which is the main part of this tutorial:

 The Javascript:

window.onload = function(){
    
    let selector = document.querySelector("#country");
    selector.addEventListener('change',function(){

        let country_id = selector.value;
        console.log(country_id)
        if(country_id == "no_country"){
            removeChilds(document.getElementById('city'));
        }
        else{
            ajax_request(country_id);
        }
        
    });


function ajax_request(id){
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
     console.log(this.responseText);
     res = JSON.parse(this.responseText)
     cities = res.cities;
     removeChilds(document.getElementById('city'));
     for(const prop in cities){
        add_option(prop,cities[prop]);
     }
    }
  };
  xhttp.open("GET", `/ajax_handler/${id}`, true);
  xhttp.send();
}


function add_option(val,text){
    var sel = document.getElementById('city');
    
// create new option element
var opt = document.createElement('option');

// create text node to add to option element (opt)
opt.appendChild( document.createTextNode(text) );

// set value property of opt
opt.value = val; 

// add opt to end of select box (sel)
sel.appendChild(opt); 
}

}

var removeChilds = function (node) {
    var last;
    while (last = node.lastChild) node.removeChild(last);
};

It looks intimidating, but it is fairly simple (believe me). I am going to explain this step by step:

Lets first explain the 2 functions named ‘add_option’ and ‘removeChilds’.

add_option adds an ‘option’ to our second (child) select box in the index.html file. It takes ‘val’ and ‘text’ as arguments, creates an <option> element with the ‘text’ and value attribute as ‘val’ and appends it to the child select box. So it takes the id of the city and its name as arguments and adds the city to our ‘Cities’ select box (dropdown).

removeChilds takes a node and deletes all its children.

 

In line 5, we listen to the ‘change’ event listener. If the value of the selected country is ‘no_country’, it means no country is selected. So we delete all the children of the ‘Cities’ selectbox (dropdown) in line 10.

If a country is selected, we call the function ajax_request with the id of that country.

ajax_request takes the id of the country, sends an xmlhttprequest to the server, receives all the corresponding cities, remove all children from the ‘cities’ selectbox and update it with the latest cities received from the server.

The full code for this can be found at github:

 https://github.com/mazharrazmian/webwithdjango/tree/master/dropdownProject

Leave a Comment

Your email address will not be published. Required fields are marked *