Set dynamic values from a dictionary for select values inside a Django template using Javascript or other method
I have three consecutive select options that their values change according to the previous select. The purpose is to categorize products using these select options. First option is to either categorize products with their usage
or model
value. If usage
is selected as the first select
option, then the second select
that is populated with usage
s list which is all objects of the Usage
model, is shown, and if model
is selected, then the select populated with all objects of MainModel
model is shown and the other select
tag gets hidden with visually-hidden
class.
To this point, my codes are as below:
views.py:
def get_common_queryset():
usage_queryset = Usage.objects.all()
main_model_queryset = MainModel.objects.all()
sub_usage_queryset = SubUsage.objects.all()
pump_type_queryset = PumpType.objects.all()
queryset_dictionary = {
"usage_queryset": usage_queryset,
"main_model_queryset": main_model_queryset,
"sub_usage_queryset": sub_usage_queryset,
"pump_type_queryset": pump_type_queryset,
}
return queryset_dictionary
def solution_main(request):
context = get_common_queryset()
return render(request, "solutions/solution_main.html", context)
my template:
<div class="col-md-3 mx-md-5">
<h2 class="h5 nm-text-color fw-bold mb-4">Choose usage or model:</h2>
<select required aria-label="Select usage or model"
id="usage_model_select" class="form-select" onchange="set_usage_or_model_dic()">
<option>----</option>
<option value="usage">Usage</option>
<option value="model">Model</option>
</select>
</div>
{# usage or model select div #}
<div class="col-md-3 mx-md-5">
{# usage select div #}
<div class="usage visually-hidden" id="usage_div">
<h2 class="h5 nm-text-color fw-bold mb-4">Select usage:</h2>
<select required aria-label="Select usage" class="form-select"
name="usage_select" onchange="set_sub_usage_list()" id="usage_select_id">
<option selected>----</option>
{% for usage in usage_queryset %}
<option value="{{ usage.id }}">{{ usage.usage_name_fa }}</option>
{% endfor %}
</select>
</div>
{# model select div #}
<div class="model visually-hidden" id="model_div">
<h2 class="h5 nm-text-color fw-bold mb-4">Select model:</h2>
<select required aria-label="Select model" class="form-select"
name="model_select" onchange="set_pump_type_list()" id="model_select_id">
<option selected>----</option>
{% for model in main_model_queryset %}
<option value="{{ model.id }}">{{ model.model_name_fa }}</option>
{% endfor %}
</select>
</div>
</div>
{# select sub_usage or pump_type div #}
<div class="col-md-3 mx-md-5">
{# sub_usage select div #}
<div class="sub_usage visually-hidden" id="sub_usage_div">
<h2 class="h5 nm-text-color fw-bold mb-4">Select sub-usage:</h2>
<select required aria-label="Select sub_usage" class="form-select" name="sub_usage_select">
<option selected>All sub-usages from this usage</option>
{% for sub_usage in sub_usage_queryset %}
<option value="{{ sub_usage.id }}">{{ sub_usage.sub_usage_name_fa }}</option>
{% endfor %}
</select>
</div>
{# model select div #}
<div class="pump-type visually-hidden" id="pump_type_div">
<h2 class="h5 nm-text-color fw-bold mb-4">Select pump type:</h2>
<select aria-label="Select pump_type" class="form-select" name="pump_type_select">
<option selected>All pump types of this model</option>
{% for pump_type in pump_type_queryset %}
<option value="{{ pump_type.id }}">{{ pump_type.type_name }}</option>
{% endfor %}
</select>
</div>
</div>
<div>
<input type="submit" value="Next" id="submit" class="btn btn-primary">
</div>
(I'm using JS to show/hide specific divs with removing/adding visually-hidden
class)
But, I don't want to use sub_usage_queryset = SubUsage.obects.all()
, I want to see which usage
(or model
) is selected in the previous stage and populate the next select
according to this choice.
The solution that I have in mind is that since there are few usage
s and main_model
s, I can create a dictionary for each that contains different usage
s or main_model
s as keys, and their sub_usage
s or pump_type
s as a list for their value. As an example for usage
s:
sub_usage_list = {}
for usage in Usage.objects.all():
usage_sub_usage_list = SubUsage.objects.filter(usage=usage)
sub_usage_list[usage] = usage_sub_usage_list
So sub_usage_list
will contain each usage
as a key, and that usage
's sub_usage
s as a list for that key's value.
I don't know if this approach is correct, and even if it is, I don't know how to use specific sub_usage list from this dictionary accoring to the selected value in the relating select
option. I'm not very familiar with JS, So I'd very much appreciate your help and tips.
NOTE: As you can see, I'm using server side rendering, so I don't know how to do this without refreshing the page.
Answer
You're on the right track with the idea of creating a dictionary on the server side to dynamically populate the second select
dropdown based on the first one. To implement this efficiently, you can leverage AJAX (Asynchronous JavaScript and XML) to make requests to the server and get the relevant data for the next select
options, without refreshing the page.
Here’s how you can achieve this step by step:
1. Create the Server-Side View for Handling AJAX Requests
You need a new view that handles AJAX requests. This view will return the filtered options based on the user’s selection. For example, when a user selects a usage
, the server will respond with the list of related sub_usage
values.
from django.http import JsonResponse
from .models import Usage, SubUsage, MainModel, PumpType
def get_sub_usage_or_pump_type(request):
# Get the selected option (either usage or model)
selected_value = request.GET.get('selected_value') # Can be 'usage' or 'model'
selected_id = request.GET.get('selected_id') # ID of the selected value
if selected_value == 'usage':
sub_usages = SubUsage.objects.filter(usage_id=selected_id)
sub_usage_data = [{'id': sub_usage.id, 'name': sub_usage.sub_usage_name_fa} for sub_usage in sub_usages]
return JsonResponse({'options': sub_usage_data})
elif selected_value == 'model':
pump_types = PumpType.objects.filter(main_model_id=selected_id)
pump_type_data = [{'id': pump_type.id, 'name': pump_type.type_name} for pump_type in pump_types]
return JsonResponse({'options': pump_type_data})
return JsonResponse({'options': []}) # If no valid option, return an empty list
2. Update the Template to Include AJAX Logic
You’ll need to add JavaScript to handle the dynamic population of the second select
box. When the first select
box (usage_model_select
) changes, the JavaScript should send an AJAX request to fetch the relevant options and populate the second dropdown accordingly.
Here’s the updated template with JavaScript:
<div class="col-md-3 mx-md-5">
<h2 class="h5 nm-text-color fw-bold mb-4">Choose usage or model:</h2>
<select required aria-label="Select usage or model"
id="usage_model_select" class="form-select" onchange="set_usage_or_model_dic()">
<option>----</option>
<option value="usage">Usage</option>
<option value="model">Model</option>
</select>
</div>
{# usage or model select div #}
<div class="col-md-3 mx-md-5">
{# usage select div #}
<div class="usage visually-hidden" id="usage_div">
<h2 class="h5 nm-text-color fw-bold mb-4">Select usage:</h2>
<select required aria-label="Select usage" class="form-select"
name="usage_select" onchange="set_sub_usage_list()" id="usage_select_id">
<option selected>----</option>
{% for usage in usage_queryset %}
<option value="{{ usage.id }}">{{ usage.usage_name_fa }}</option>
{% endfor %}
</select>
</div>
{# model select div #}
<div class="model visually-hidden" id="model_div">
<h2 class="h5 nm-text-color fw-bold mb-4">Select model:</h2>
<select required aria-label="Select model" class="form-select"
name="model_select" onchange="set_pump_type_list()" id="model_select_id">
<option selected>----</option>
{% for model in main_model_queryset %}
<option value="{{ model.id }}">{{ model.model_name_fa }}</option>
{% endfor %}
</select>
</div>
</div>
{# select sub_usage or pump_type div #}
<div class="col-md-3 mx-md-5">
{# sub_usage select div #}
<div class="sub_usage visually-hidden" id="sub_usage_div">
<h2 class="h5 nm-text-color fw-bold mb-4">Select sub-usage:</h2>
<select required aria-label="Select sub_usage" class="form-select" name="sub_usage_select">
<option selected>All sub-usages from this usage</option>
<!-- The options will be populated dynamically by AJAX -->
</select>
</div>
{# pump-type select div #}
<div class="pump-type visually-hidden" id="pump_type_div">
<h2 class="h5 nm-text-color fw-bold mb-4">Select pump type:</h2>
<select aria-label="Select pump_type" class="form-select" name="pump_type_select">
<option selected>All pump types of this model</option>
<!-- The options will be populated dynamically by AJAX -->
</select>
</div>
</div>
<div>
<input type="submit" value="Next" id="submit" class="btn btn-primary">
</div>
<script>
function set_usage_or_model_dic() {
var selectedOption = document.getElementById('usage_model_select').value;
// Show/hide the appropriate select boxes
if (selectedOption === 'usage') {
document.getElementById('usage_div').classList.remove('visually-hidden');
document.getElementById('model_div').classList.add('visually-hidden');
document.getElementById('sub_usage_div').classList.add('visually-hidden');
document.getElementById('pump_type_div').classList.add('visually-hidden');
} else if (selectedOption === 'model') {
document.getElementById('model_div').classList.remove('visually-hidden');
document.getElementById('usage_div').classList.add('visually-hidden');
document.getElementById('sub_usage_div').classList.add('visually-hidden');
document.getElementById('pump_type_div').classList.add('visually-hidden');
}
}
function set_sub_usage_list() {
var usageId = document.getElementById('usage_select_id').value;
if (usageId) {
fetch(`/get_sub_usage_or_pump_type/?selected_value=usage&selected_id=${usageId}`)
.then(response => response.json())
.then(data => {
var subUsageSelect = document.querySelector('select[name="sub_usage_select"]');
subUsageSelect.innerHTML = "<option selected>All sub-usages from this usage</option>"; // Reset options
data.options.forEach(function(subUsage) {
var option = document.createElement('option');
option.value = subUsage.id;
option.text = subUsage.name;
subUsageSelect.appendChild(option);
});
document.getElementById('sub_usage_div').classList.remove('visually-hidden');
});
}
}
function set_pump_type_list() {
var modelId = document.getElementById('model_select_id').value;
if (modelId) {
fetch(`/get_sub_usage_or_pump_type/?selected_value=model&selected_id=${modelId}`)
.then(response => response.json())
.then(data => {
var pumpTypeSelect = document.querySelector('select[name="pump_type_select"]');
pumpTypeSelect.innerHTML = "<option selected>All pump types of this model</option>"; // Reset options
data.options.forEach(function(pumpType) {
var option = document.createElement('option');
option.value = pumpType.id;
option.text = pumpType.name;
pumpTypeSelect.appendChild(option);
});
document.getElementById('pump_type_div').classList.remove('visually-hidden');
});
}
}
</script>
3. Explanation of the Changes:
-
AJAX Call for Dynamic Data:
- When the user selects a
usage
ormodel
, an AJAX request is sent to the/get_sub_usage_or_pump_type/
view, which returns the filtered options in JSON format. - The
set_sub_usage_list()
function is triggered when theusage_select
dropdown changes, and theset_pump_type_list()
function is triggered when themodel_select
dropdown changes. - The server responds with a list of
sub_usage
orpump_type
objects, which are used to populate the corresponding dropdown.
- When the user selects a
-
Displaying and Hiding Selects Dynamically:
- The function
set_usage_or_model_dic()
controls the visibility of the usage/model-related dropdowns, showing or hiding them based on the user's selection.
- The function
-
Populating Select Options:
- The JavaScript dynamically creates
<option>
elements and appends them to the corresponding select elements based on the response from the server.
- The JavaScript dynamically creates
4. Update the URLs
In urls.py
, make sure you add the new view for AJAX requests:
from django.urls import path
from . import views
urlpatterns = [
path('get_sub_usage_or_pump_type/', views.get_sub_usage_or_pump_type, name='get_sub_usage_or_pump_type'),
# other URLs
]
This solution allows you to dynamically update the second and third select
dropdowns based on the first one, without refreshing the page, using AJAX to handle the communication between the front-end and back-end.