[Django]-Django & ajax dependant html select lists (cascade drop down lists)

4👍

When I face this issue I use AJAX to make 2 selects dependants. First I use a Django Form like:

# I avoid the importation of the choices to make answer shorter
class YourForm(forms.Form):    
    industry = forms.ChoiceField(choices=USER_PROFILE_CURRENT_INDUSTRY_TYPES)
    sector = forms.ChoiceField(choices=USER_PROFILE_CURRENT_SECTOR_TYPES)    
    # ... other fields

I will avoid the basics of the views (how to manage GET/POST methods) and the basic HTML django form, I’ll go directly to the AJAX function:

Let’s assume the selectors ID’s are: #id_sector and #id_industry

function get_industry(){
            jQuery.ajax({
              async: false,
              type: "POST",
              url: "/a/get/industry/",
              data: "sector_id=" + $('#id_sector').val(),
              success: function(response) {
                    result = JSON.parse(response);
                    if (result) {
                        // I usually receive a list of items here
                        // I use this list to replace the dependant select                                                

                        $('#id_industry').empty()  // Use to empty the select

                        // Now we append the industry options we've received
                        for(var i=0;i < result.item_list.length;i++){
                            $('#id_industry').append($('<option>', { 
                                value: result.item_list[i]['id'],
                                text: result.item_list[i]['name'] 
                        }));      
                        }

                    } else {
                        console.log('error');
                    }
                }
            });
        }

function get_sector(){
            jQuery.ajax({
              async: false,
              type: "POST",
              url: "/a/get/sector/",
              data: "industry_id=" + $('#id_industry').val(),
              success: function(response) {
                    result = JSON.parse(response);
                    if (result) {
                        $('#id_sector').empty()  // Use to empty the select

                        // Now we append the sector options we've received
                        for(var i=0;i < result.item_list.length;i++){
                            $('#id_sector').append($('<option>', { 
                                value: result.item_list[i]['id'],
                                text: result.item_list[i]['name'] 
                        }));                                                   
                        }

                    } else {
                        console.log('error');
                    }
                }
            });
        }

Now I’m going to show the AJAX view. You can set your AJAX view in a file called ajax.py:

from yourapp.models import USER_PROFILE_CURRENT_SECTOR_TYPES

INDUSTRY_DICT = {
    4: range(14,36),
    5: range(36,58),
    6: range(58,80),
    7: range(80,102),
    8: range(102,124),
    10: range(124,146)  # This is the only true equivalence that you passed to me
}

@csrf_exempt
def get_sectors(request):
    response = []
    industry_id = int(request.POST['industry_id'])

    # With the sector_id you know wich sector the user has selected
    # You should generate the list based in your needs
    data = []
    if industry_id:
        sectors = INDUSTRY_DICT[industry_id]  # This return a list of ID's of sectors
        # Then make loop over all sectors
        for sector_id in sectors:  
            # To get the sector name you should use another dict
            # I think you already have it in USER_PROFILE_CURRENT_SECTOR_TYPES
            # Remember to import it (check above)
            sector_name =  USER_PROFILE_CURRENT_SECTOR_TYPES[sector_id]
            # We append the id and the name to replace options in the HTML
            data.append({'id':sector_id, 'name':sector_name})  

        response = { 'item_list':data }  # We send back the list
        return HttpResponse(simplejson.dumps(response))

    # If we get any error, or cannot get the sector, we send an empty response
    response = {}
    return HttpResponse(simplejson.dumps(response))

I will avoid adding a second AJAX function ‘get_sectors‘ because I supose you understand the logic, it should be the same as this function, but you’ll receive industry_id instead of sector_id, I think you can face with the second function.

The last step before setting up the urls is to define the functions that manage the changes on the selects and call the AJAX functions:

    $("#id_sector").change(function(){           
            get_industry();  // AJAX function call when sector is selected           
    });

    $("#id_industry").change(function(){           
            get_sector();  // AJAX function call when industry is selected             
    });

You’ll need to add both urls in your urls.py:

# ... YOUR OTHER URLS
url(r'^a/get/industry/?$', 'yourproject.ajax.get_industries', name='get_industries'),
url(r'^a/get/sector/?$', 'yourproject.ajax.get_sectors', name='get_sectors'),

Tips:

  • When you see $('#id_industry'), this refers to the industry selector ID, same as $('#id_sector') refers to the sector selector ID

  • What I call AJAX function goes in the HTML template

  • What I call AJAX view goes in a .py file, I usually add AJAX views to ajax.py file
  • For this example I added @csrf_exempt in the AJAX view, If you want to know how to manage CSRF token in AJAX functions, you can go to: CSRF & AJAX: Official Docs
  • In the AJAX view you have the Sector the user has selected:
    • You need to generate a list of industries
    • The list will be used to generate the options for the select
    • You can do this in so many different ways, you should choose the one that fits your scenario better.
    • I’m sending the ID, but you can send the name if it’s better for you

Edit: Filter Sector/Industry

  • get_industries(request) changed to get_sectors(request)

First of all you should have the equivalences, you can have them in different ways, you could have 2 models Industry and Sector, and relate them using Foreign Keys but I think you don’t have models so you could have 2 dictionaries, one for industries and one for sector.

I will supose the equivalences, you should change the values of the dict for the ones you need:

INDUSTRY_DICT = {
4: range(14,36),
5: range(36,58),
6: range(58,80),
7: range(80,102),
8: range(102,124),
10: range(124,146)  # This is the only equivalence that you passed to me
}

This will generate a dict, and if you do INDUSTRY_DICT[industry_id] it will return the ID list of sectors that should appear in the select. Check the function above with the new changes.

  • I also recommend you to have 2 more functions to “restart” both selects and append all the possible options again in case the user would like to change his choice

Leave a comment