How popular select from another select using python and Django?

Asked

Viewed 2,185 times

4

Good evening, everyone!

I’m new to the forum and started doing a little project with python and Django, where I am trying to fill a select according to the option selected in another. However, as a beginner, I don’t know how to do this.

<div class="form-group col-sm-4">
   <label id="lb-shift">Turno</label>
       <select id="p-shift" class="form-control">
        {% for course in courses %}
           <option value="{{ course.id }}">{{ course.name }}</option>
        {% endfor %}
       </select>
</div>
<div class="form-group col-sm-4">
    <label id="lb-shift">Turno</label>
    <select id="p-shift" class="form-control">
        {% for turm in turms %}
             {% if turm.idCourse == "valor do outro select"%}
                <option value="{{ turm.id }}">{{ turm.name }}</option>
             {% endif %}
        {% endfor %}
    </select>
</div>
  • If it is during client-side interaction, no request is made to the server between the first selection and the population of the second select you should use is javascript, with ajax if necessary

  • Both select have the same id. Pay attention to that.

2 answers

4

To understand why you can’t implement the way you tried, only with Python, you’ll need to understand the differences between frontend and backend. Maybe this discussion help you with something.

But basically Python runs on backend, delivering the HTTP response to the client, in this case the browser. After the reply is delivered, the execution is completed and therefore Python will no longer respond to changes on the page, such as selecting an item on select. You can make it respond to an asynchronous request, through AJAX, but the easiest in your case is to implement logic in another way, with Javascript.

First, you define, second select, an attribute data-course for each option, storing the value of turn.idCourse, as follows:

<div class="form-group col-sm-4">
   <label id="lb-shift">Turno</label>
       <select id="p-shift" class="form-control">
        {% for course in courses %}
           <option value="{{ course.id }}">{{ course.name }}</option>
        {% endfor %}
       </select>
</div>
<div class="form-group col-sm-4">
    <label id="lb-shift">Turno</label>
    <select id="t-shift" class="form-control">
        {% for turm in turms %}
            <option data-course="{{ turn.idCourse }}" value="{{ turm.id }}">{{ turm.name }}</option>
        {% endfor %}
    </select>
</div>

And with Javascript, you watch the event onchange of the first select, recovers the selected value and displays in the second select, only the option whose value in data-course is equal to the selected value.

The HTML code used below is a representation close to the HTML that Python would generate dynamically and only serves to reproduce the example here.

$(function () {

  // Oculta as opções do segundo select:
  $("#t-shift option").hide();
  
  // Observa o evento change do primeiro select:
  $("#p-shift").on("change", function () {
  
    // Recupera o valor selecionado:
    let course = $("#p-shift").val();
    
    // Oculta as opções atuais:
    $("#t-shift option").hide();
    
    // Exibe as opções conforme a seleção:
    $("#t-shift option[data-course="+ course +"]").show();
  
  });

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="form-group col-sm-4">
  <label id="lb-shift">Course</label>
  <select id="p-shift" class="form-control">
    <option selected disabled>Selecione</option>
    <option value="1">Course 1</option>
    <option value="2">Course 2</option>
    <option value="3">Course 3</option>
   </select>
</div>
<div class="form-group col-sm-4">
  <label id="lb-shift">Turn</label>
  <select id="t-shift" class="form-control">
    <option selected disabled>Selecione</option>
    <option data-course="1" value="1">Turn 1</option>
    <option data-course="2" value="2">Turn 2</option>
    <option data-course="3" value="3">Turn 3</option>
    <option data-course="1" value="4">Turn 4</option>
    <option data-course="2" value="5">Turn 5</option>
    <option data-course="3" value="6">Turn 6</option>
  </select>
</div>

Check that when selecting the Course 1, only the options Turn 1 and Turn 4 will be available. By selecting the Course 2, options will be available Turn 2 and Turn 5. Finally, when selecting Course 3, will be the options Turn 3 and Turn 6.

1

A path (perhaps the most obvious) is the use of javascript, in the first select you arrow an onchange that calls a function to "popular" the second select.

Has a demo here which fits perfectly with your question, just make the appropriate adaptations. Do not be intimidated by the code in the javascript section (bottom). The guy created arrays with several countries/ states of the world, so it became so extensive, go straight down in the two functions (populateStates and populateCountries).

Basically, he did the following, calls these two functions when the page is loaded and populates the two selects (as the country has not yet been chosen, the states select is empty)on that occasion, in the function populateCountries it changes the onchange of countries select to call the function populating the states (populateStates) sending the objects to it.

Try to adapt, if you can not, post here the problem.

Edited from 20/03/2017 ( from here)
Ok, As agreed (in the comments of that reply), I spent my Saturday to create my own version to try to answer what was requested. The explanation here is only to clarify how the process was to take the database data, pass to the Jango template and, finally, deliver to the JS. The complete and functional project (in Django) can be downloaded in this github.

In the example we will use a register of vehicles in a fleet (see that I was not busy with recording the data in the database, focused only on what was requested in the question). There are two main models for the demonstration, one for the brands (Brands) and the other for the models of the vehicles (Car).

py.models

from django.db import models
import json

class Brand(models.Model):
    company_name = models.CharField(max_length=100)

    def __str__(self):
        return self.company_name

class Car(models.Model):
    brand = models.ForeignKey(Brand)
    name = models.CharField(max_length=100)

    def __str__(self):
        return {'name': self.name, 'brand': self.brand.company_name}

It is important to note that an artificial function was used in the __str__ function of the model Car, so that the call to Objects.all() returns a dictionary with the name and brand of the models.


With the models ready, we go to the view, which is the main function to make the data available to the template and, consequently, to JS.

import json
from django.shortcuts import render
from .models import Brand, Car

def regcar(request):
    brands = Brand.objects.all()
    cars = Car.objects.all()
    dcars = {}
    for car in cars:
        brand = str(car.brand)
        if brand in dcars:
            dcars[brand].append(car.name)
        else:
            dcars[brand] = [car.name]
    cars = json.dumps(dcars)
    brands = json.dumps([str(b) for b in brands])
    return render(request, 'core/regcar.html', {'brands': brands, 'cars': cars})

This view does, essentially the seginte:

  • Select from models the Brand objects (tags) and assign to the variable Brands;
  • Select from models the Car objects (models) and assign to variable Cars;
  • Convert the two variables to JSON format;
  • Render a regcar template.

The template is very simple, has only one div with the two selects, properly identified by the ID. Although it is presented separately here, the JS script is included in the template, so that it is loaded with it (could be via a block, WYL.). In the load process, select identified as 'Brand' will be loaded by a JS function.

{% load staticfiles %}

<!DOCTYPE html>
<html lang="pt-br">
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
        <link href="{% static "css/style.css" %}" rel="stylesheet">
        <meta charset="UTF-8">
        <title>Populating a Select with Django</title>
    </head>

    <body>

        <h1>Populate one dropdown based on selection in another.</h1>
        <p>Change the contents of dropdown Car based on the selection in dropdown Brand, using Django + Javascript:</p>

        <div class="select-style">
            <br />Brand:
            <select required id="brand">
            </select>

            <br />
            <br />Car:
            <select id="car" >
            </select>
        </div>


    </body>
</html>

The javascript code is loaded with the template at this time two variables are created that receive the JSON values that were sent to the template by the view, see that just do the assignment normally, as if it were in python or in the template itself. In addition to creating these variables, the brand select (Brand) onchange event is directed to the populateCar() function, where the select whose id=car (car models) is populated every time the select Brand onchange is modified. Finally, the function populateBrand() is called to fill the select of the marks (Brand).

<script language="javascript">
    $('#brand').change(function() {populateCar()});
    var cars = {{ cars|safe }}
    var brands = {{ brands|safe }}
    populateBrand();

    function populateBrand() {
        $("#brand").empty();
        $("#brand").append('<option value="" disabled selected>Select your option</option>');
        $.each(brands, function(v) {
            $('#brand')
                .append($("<option></option>")
                .attr("value", brands[v])
                .text(brands[v]));
        })
    }

    function populateCar(event) {
        brand = $("#brand option:selected" ).text();
        $("#car").empty();

        $("#car").append('<option value="" disabled selected>Select your option</option>');
        for (let [b, bcars] of Object.entries(cars)) {
            if (b==brand) {
                for (car in bcars) {
                    $('#car')
                        .append($("<option></option>")
                        .attr("value", bcars[car])
                        .text(bcars[car]));
                }
            }
        }

    }

</script>

Layout (Firefox)

Resultado no Firefox


Links:

That same question in Stoen:
https://stackoverflow.com/a/43074402/2879341

Full project in Django:
http://jsfiddle.net/bdhacker/eRv2W/

Codepen version:
https://codepen.io/Sidon/pen/yMpbKg

Fiddle version:
https://jsfiddle.net/Sidon/6m1nf0zu/62/

  • His answer is not wrong, but see that he asked how to do in python, the ideal would be to create the home screen the way he set up the select, from the onchange of the first select, call with js a web server that mounts the second select, or with content already rendered somewhere on the page, so that when it changes, it is not necessary to consult the web server again

  • As his question was "tagged" with Django, I answered thinking of Jango development, it is not impossible but it is very difficult to develop in Jango only with python, I had the same problem as him and solved with javascript insertion. Ok, want to use websever? I see no need, I think the question is much simpler.

  • I have no experience with Django, even in Python my experience is almost null :P The suggestion was something like rendering both selects with all content, and according to the selected in the first select, go changing the content of the second select (dai yes com js)

  • Or use ajax, if the content of the second select comes from the server (which seems to be the case), then the structure would be very similar to the one you proposed

  • Exactly, in fact the JS is only to take advantage of the onchange, everything else will be done in python, as well as it is necessary to use html in Django, sometimes, many web platform technologies such as JS, ajax, etc, if it is necessary.

  • In my suggestion, no matter that the content comes from the server (in Django usually comes from a BD), it will work the same way, just use the Django templates to "render" the content.

  • 1

    Do the following: leave the link you posted only as a reference and create a verifiable example here, using the same idea. This will improve the quality of your response greatly.

  • Eheheh, I even agree @Andersoncarloswoss, but in my opinion, it would win more who asked if you tried to fish instead of winning the fish. Anyway put in the fds, if applicable.

  • Ok, as suggested, created the "verifiable" example, using the same idea and posted the explanation here, hopefully it can help.

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.