Reputation: 1245
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.
Upvotes: 3
Views: 4815
Reputation: 10563
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'),
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
.py
file, I usually add AJAX views to ajax.py
file@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 Docsget_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.
Upvotes: 4