简体   繁体   中英

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.

To achieve what you want without refreshing the page it is necessary to use AJAX.

Starting with your template. Since you are going to populate dynamically based on choices, it is not necessary to have one box for each QuerySet. You can clear and repopulate them using JS.

templates.html:

<body>
    <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="populate_lists()">
            <option value="" selected>----</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 options:</h2>
            <select required aria-label="Select usage" class="form-select"
                    name="usage_select" onchange="populate_lists()" id="select_options">
                <option value="" selected>----</option>
            </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">Sub selection:</h2>
            <select required aria-label="Select sub_usage" class="form-select" id="sub_usage_select" name="sub_usage_select">
                <option selected>All sub-selections</option>
            </select>
        </div>
    </div>

    <div>
        <input type="submit" value="Next" id="submit" class="btn btn-primary">
    </div>
</body>

templates.html <script> : ( CSRF with AJAX | Get Select element | Populate Select element | Clear Select element )

<script>
    function getCookie(name) {
        let cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            const cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                const cookie = cookies[i].trim();
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }

    // Clear SelectionBoxes
    function removeOptions(selectElement, initial) {
        var i, L = selectElement.options.length - 1;
        for(i = L; i >= 0; i--) {
            selectElement.remove(i);
        }
        var opt = document.createElement('option');
        opt.value = "";
        opt.innerHTML = initial;
        selectElement.appendChild(opt);
    }

    function populate_lists() {
        const url = '/solution/ajax/';
        const csrftoken = getCookie('csrftoken');

        var e = document.getElementById("usage_model_select");
        var selectedOption = e.value;
        if (selectedOption == "" ) {
            removeOptions(document.getElementById("select_options"), "----");
            removeOptions(document.getElementById("sub_usage_select"), "All sub-usages from this usage");
            return
        }

        var e = document.getElementById("select_options");
        var option = e.value;
        if (option == "" ) {
            option = null
        }
        
        // Send AJAX Request
        fetch(url, {
            method: 'POST',
            headers: {
                'X-CSRFToken': csrftoken,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                "select": selectedOption,
                "option": option,
            }),
        })
        .then((response) => response.json())
        .then((data) => {
            var select = document.getElementById("select_options");
            removeOptions(select, "----");
            var selected_usage_name = null

            for (var i = 0; i<=data.select.length-1; i++){
                var opt = document.createElement('option');
                opt.value = data.select[i]['id'];
                opt.innerHTML = data.select[i]['name'];
                
                if (data.select[i]['id'] == data.selected_option) {
                    selected_usage_name = data.select[i]['name'];
                    opt.selected = true
                }
                select.appendChild(opt);
            }

            if (data.sub_select != null) {
                var select = document.getElementById("sub_usage_select");
                removeOptions(select, `All sub-usages from ${selectedOption} ${selected_usage_name}`);

                for (var i = 0; i<=data.sub_select.length-1; i++){
                    var opt = document.createElement('option');
                    opt.value = data.sub_select[i]['id'];
                    opt.innerHTML = data.sub_select[i]['name'];
                    select.appendChild(opt);
                }
                console.log(data.sub_select)
            }
            
        })
        .catch((error) => {
            console.error('Error:', error);
        });
    }
</script>

views.py:

def solution_ajax(request):
    data = json.loads(request.body)
    sub_select = None

    if data['select'] == 'usage':
        select = list(Usage.objects.all().values())

        if data['option'] is not None:
            sub_select = list(SubUsage.objects.filter(usage__id=data['option']).values())

    elif data['select'] == 'model':
        select = list(MainModel.objects.all().values())

        if data['option'] is not None:
            sub_select = list(PumpType.objects.filter(model__id=data['option']).values())

    return JsonResponse({
        'select': select,
        'selected_option': data['option'],
        'sub_select': sub_select,
    })
    

def solution_main(request):
    return render(request, "solutions/solution_main.html") 

Note, I've made some extra features such as clearing the select options, although not for all events.

Merry xmas.

There is a method in django using django-select2. See the soloution of this post: How to populate a select list in a Django Template dynamically using Javascript?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM