3

I am new to Django (i am using Django 1.4 & python 2.7) and cannot understand how to accomplish the following issue.

I have done a lot of searching on SO & Google, and so far become very confused on how to do this problem.

I have two html select lists on a form - Industry & Sector. When a user selects Engineering from the Industry html select list, the Sector html select list should be dynamically filled with only Engineering options. The same with other selections made on the Industry html select list. The Sector html select list should be filled dynamically without a page refresh - so I am assuming that JQuery / AJAX will have to be used.

I am not so sure I have correctly set up the UserProfile model below. The Industry & Sector html select lists appear on the form and work, but are not dependent on each other - they are separate models.PositiveIntegerField fields. Perhaps the Industry & Sector values should be foreign keys on the UserProfile model below. I need some advice on this.

Here is my user models.py file:

class UserProfile(models.Model):

    SELECT_INDUSTRY = 0
    ACCOUNTING = 1
    ADMINISTRATION_OFFICE_SUPPORT = 2
    BANKING_FINANCIAL_SERVICES = 3
    CALL_CENTRE_CUSTOMER_SERVICE = 4
    COMMUNITY_SERVICES_DEVELOPMENT = 5
    CONSTRUCTION = 6
    CONSULTING_STRATEGY = 7
    DESIGN_ARCHITECTURE = 8
    EDUCATION_TRAINING = 9
    ENGINEERING = 10
    EXECUTIVE_GENERAL_MANAGEMENT = 11
    FARMING_ANIMALS_CONSERVATION = 12
    GOVERNMENT_DEFENCE = 13
    GRADUATE_ENTRY_LEVEL = 14
    HEALTHCARE_MEDICAL = 15
    HOSPITALITY_TRAVEL_TOURISM = 16
    HUMAN_RESOURCES_RECRUITMENT = 17
    INSURANCE_SUPERANNUATION = 18
    INFORMATION_TECHNOLOGY_TELECOMMUNICATIONS = 19
    LEGAL = 20
    MANUFACTURING = 21
    MARKETING_COMMUNICATIONS = 22
    MEDIA_ADVERTISING_ARTS_ENTERTAINMENT = 23
    MINING_RESOURCES_ENERGY = 24
    REAL_ESTATE_PROPERTY = 25
    RETAIL_CONSUMER_PRODUCTS = 26
    SALES = 27
    SCIENCE_TECHNOLOGY = 28
    SELF_EMPLOYMENT = 29
    SPORT_RECREATION = 30
    TRADES_SERVICES = 31
    TRANSPORT_LOGISTICS = 32


    USER_PROFILE_CURRENT_INDUSTRY_TYPES = (
        (SELECT_INDUSTRY, _('Select Current Industry')),
        (ACCOUNTING, _('Accounting')),
        (ADMINISTRATION_OFFICE_SUPPORT, _('Administration & Office Support')),
        (BANKING_FINANCIAL_SERVICES, _('Banking & Financial Services')),
        (CALL_CENTRE_CUSTOMER_SERVICE, _('Call Centre & Customer Service')),
        (COMMUNITY_SERVICES_DEVELOPMENT, _('Community Services & Development')),
        (CONSTRUCTION, _('Construction')),
        (CONSULTING_STRATEGY, _('Consulting & Strategy')),
        (DESIGN_ARCHITECTURE, _('Design & Architecture')),
        (EDUCATION_TRAINING, _('Education & Training')),
        (ENGINEERING, _('Engineering')),
        (EXECUTIVE_GENERAL_MANAGEMENT, _('Executive & General Management')),
        (FARMING_ANIMALS_CONSERVATION, _('Farming, Animals & Conservation')),
        (GOVERNMENT_DEFENCE, _('Government & Defence')),
        (GRADUATE_ENTRY_LEVEL, _('Graduate / Entry Level')),
        (HEALTHCARE_MEDICAL, _('Healthcare & Medical')),
        (HOSPITALITY_TRAVEL_TOURISM, _('Hospitality, Travel & Tourism')),
        (HUMAN_RESOURCES_RECRUITMENT, _('Human Resources & Recruitment')),
        (INSURANCE_SUPERANNUATION, _('Insurance & Superannuation')),
        (INFORMATION_TECHNOLOGY_TELECOMMUNICATIONS, _('Information Technology & Telecommunications')),
        (LEGAL, _('Legal')),
        (MANUFACTURING, _('Manufacturing')),
        (MARKETING_COMMUNICATIONS, _('Marketing & Communications')),
        (MEDIA_ADVERTISING_ARTS_ENTERTAINMENT, _('Media, Advertising, Arts & Entertainment')),
        (MINING_RESOURCES_ENERGY, _('Mining, Resources & Energy')),
        (REAL_ESTATE_PROPERTY, _('Real Estate & Property')),
        (RETAIL_CONSUMER_PRODUCTS, _('Retail & Consumer Products')),
        (SALES, _('Sales')),
        (SCIENCE_TECHNOLOGY, _('Science & Technology')),
        (SELF_EMPLOYMENT, _('Self Employment')),
        (SPORT_RECREATION, _('Sport & Recreation')),
        (TRADES_SERVICES, _('Trades & Services')),
        (TRANSPORT_LOGISTICS, _('Transport & Logistics'))
    )


    SELECT_SECTOR_TYPE = 0
    _ALL_ACCOUNTING_JOBS = 1
    ....(culled for brevity)
    _ALL_ENGINEERING_JOBS = 124
    AEROSPACE_ENGINEERING = 125
    AUTOMOTIVE_ENGINEERING = 126
    BUILDING_SERVICES_ENGINEERING = 127
    CHEMICAL_ENGINEERING = 128
    CIVIL_STRUCTURAL_ENGINEERING = 129
    ELECTRICAL_ELECTRONIC_ENGINEERING = 130
    ENGINEERING_DRAFTING = 131
    ENVIRONMENTAL_ENGINEERING = 132
    FIELD_ENGINEERING = 133
    INDUSTRIAL_ENGINEERING = 134
    MAINTENANCE = 135
    MANAGEMENT = 136
    MATERIALS_HANDLING_ENGINEERING = 137
    MECHANICAL_ENGINEERING = 138
    PROCESS_ENGINEERING = 139
    PROJECT_ENGINEERING = 140
    PROJECT_MANAGEMENT = 141
    SUPERVISORS = 142
    SYSTEMS_ENGINEERING = 143
    WATER_WASTE_ENGINEERING = 144
    OTHER_ENGINEERING_JOBS = 145
    _ALL_EXECUTIVE_GENERAL_MANAGEMENT_JOBS = 146
    ....(culled for brevity)
    OTHER_TRANSPORT_LOGISTICS_JOBS = 462


    USER_PROFILE_CURRENT_SECTOR_TYPES = (
        (SELECT_SECTOR_TYPE, _('Select Current Sector')),
        .......(culled for brevity)
        (OTHER_TRANSPORT_LOGISTICS_JOBS, _('Other Transport & Logistics Jobs'))
    )

    user = models.OneToOneField(User)
    ....(culled for brevity)
    current_industry_type = models.PositiveIntegerField(choices=USER_PROFILE_CURRENT_INDUSTRY_TYPES, default=SELECT_INDUSTRY, validators=[MinValueValidator(1)])
    current_sector_type = models.PositiveIntegerField(choices=USER_PROFILE_CURRENT_SECTOR_TYPES, default=SELECT_SECTOR_TYPE, validators=[MinValueValidator(1)])
    ....(culled for brevity)
    .

I have seen django-smart-selects, but I am not sure this is a dynamic solution and I am not sure if I have to add in separate models for the Industry & Sector & then add the foreign keys for the Industry & Sector to the UserProfile model above.

I am hoping I can somehow easily get the Industry & Sector html select lists dependent on each other with AJAX or JQuery.

Any advice and help would be appreciated.

1 Answer 1

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
Sign up to request clarification or add additional context in comments.

5 Comments

Liarez, thanks for such a detailed answer. Not sure how to code the: # Here you should add your code to get industries # I supose you'll use sector to filter the industries # You should get industries list in variable 'industries'. I am unsure how to get the industries list and then filter the sectors. I can make bounty question if you will extend answer for me.
@user3354539 What you should do in that part is to filter the industries based in one sector. If you tell me the logic to filter industries using one sector or viceversa I can finish the function
Liarez, if user selects the industry value of 10 - Engineering, then the sectors that should populate the sector select list are sector values of 124 to 145. I am unsure how to associate these two values (10 and 124-145) together.
@user3354539 ok ! give me some minutes and I'll add that part that is left, I'll just add an small example, you should implement a little more, but you'll know how to do it
@user3354539 check the Edit at the bottom of the answer, and the changes on the function get_sectors

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.