Dropdown dependent on each other

Asked

Viewed 1,158 times

4

I’m using a function taken from this site:

https://github.com/nodet07/Django-Related-DropDowns

The function works perfectly, when I select an option in a dropdown, in the 2nd Dropdown already brings me what I want, the only problem is after clicking the button Submit that does not bring me the selected fields of these dropdowns, I believe I am not knowing how to use the POST method in javascript. I don’t have much knowledge in javascript/ajax. Below is a small part of an example of what I’m trying to do:

Model

class Regiao(models.Model):    
    marca=models.IntegerField('Marca', null=True, blank=True,)
    cor=models.ForeignKey('cor.Cor', verbose_name=u'Cores', null=True, blank=True,)

Form

class RegiaoForm(forms.ModelForm):
    class Meta:
        model=Regiao
    fields=['marca', 'cor']

Views

form = RegiaoForm(request.POST or None)

    if form.is_valid():
        saveMarca = request.POST['marca']

        return redirect('apps.past.views.marcas')

marcas=Marca.objects.all()

return render_to_response('marca.html', {'form':form, 'marcas':marcas}, context_instance=RequestContext(request))

def log_getdetails(request):
    selected_marca = ""

    marca_name = request.GET['cnt']

    result_set = []
    all_cores = []

    marca_name = marca_name.split()
    marca_name = str(marca_name[2])

    selected_marca = Marca.objects.get(name=marca_name)
    all_cores = selected_marca.cor_set.all()
    for cor in all_cores:
        result_set.append({'name': cor.name})

    return HttpResponse(json.dumps(result_set), mimetype='application/json', content_type='application/json')

Template

JAVASCRIPT

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="http://yourjavascript.com/7174319415/script.js"></script>

    <script>
        $(document).ready(function(){
             $('select#selectmarcas').change(function () {
                 var optionSelected = $(this).find("option:selected");
                 //var valueSelected  = $(this.result).find("option:selected");
                 var marca_name   = optionSelected.text();


                 data = {'cnt' : marca_name};
                 ajax('/getdetails',data,function(result){
                        console.log(result);
                        $("#selectcores option").remove();
                        $("#selectcores").append('<option>'+'TODOS'+'</option>');
                        for (var i = result.length - 1; i >= 0; i--) {
                            $("#selectcores").append('<option>'+ result[i].name +'</option>');
                        };

                     });
             });
        });
</script>

HTML

{% block content %}
    <form class="form-horizontal" method="post">
        {% csrf_token %}
            {% load add_attr %}
<select class="form-control" name="selectmarcas" id="selectmarcas" >
                    <option value="">TODAS</option>
                    {% for marca in marcas %}
                        <option value="{{marca.id }}">{{ marca.name }}</option>
                    {% endfor %}
                </select>
                </div>
            </div>
            <br/>
            <div class="row {% if form.cor.errors %}has-error{% endif %}">
            <label class="col-md-2 control-label" for="{{form.cor.auto_id }}">
                {{form.cor.label }}
            </label>
                <div>
                    <select class="form-control" name ="selectcores" id="selectcores">
                        <option value="">TODAS</option>
                    </select>
           <button type="submit" class="btn btn-primary">Salvar</button>
</form>
{% endblock %}

What I need is that besides doing a dropdown dependent on the other is being able to save the information I selected after giving Ubmit. I’m sorry if I put too much information, but I’m trying to clarify my question.

1 answer

1

I had a similar problem and after hours and hours of research, I decided to implement my version. I’ll explain briefly here, I made the full and functional version available on github, see the links at the end of this reply.

Consider for this answer a registration of vehicles in a fleet, where we have 3 tables: Brands (Brand), Models of vehicles (Car) and Fleet (Fleet).

See the html+javascript version below, and then the explanation of the solution in Django:

// By Sidon | 2017
// Fiddle version: https://jsfiddle.net/Sidon/6m1nf0zu/62/
$(document).ready(function(){

    $('#brand').change(function() {populateCar()});  
  
    var ford = ['Fiesta', 'Focus', 'Fusion', 'Taurus', 'Mustang'];
    var vw = ['Passat', 'Tiguan', 'Golf', 'Jetta', 'Up']
    var fiat = ['Punto', '500', '500 City', 'Panda', 'Doblô']
    var cars =  {'Ford': ford, 'Volks': vw, 'Fiat': fiat}
    var brands = ['Ford','Fiat', 'Volks']
    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();
        for (let [k, v] of Object.entries(cars)) {
            if(k==brand) {
                for (car in cars[brand]) {
                    var opt = document.createElement("option");
                     $('#car')
                         .append($("<option></option>")
                         .attr("value", cars[brand][car])
                         .text(cars[brand][car]));
                }
            };
        }
    }
 
});
body {
  margin-left: 30px
}

.select-style {
    width: 320px;
    height: 100%;
    border-radius: 3px;
}

.select-style select {
    padding: 5px 8px;
    width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- By Sidon | 2017
Fiddle version: https://jsfiddle.net/Sidon/6m1nf0zu/62/ -->
<body>

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

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

From this section on, the description of the solution in Django


py.models

from django.db import models

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 brand_name(self):
        return self.brand.company_name

    def __str__(self):
        return self.name


class Fleet(models.Model):
    car = models.ForeignKey(Car)
    description = models.CharField(max_length=100)

    def car_name(self):
        return self.car.name

    def brand(self):
        return self.car.brand.company_name

    def __str__(self):
        return self.description

The goal is to register the cars in the fleet (Fleet). See that only the fields car (foreign key) and description will be actually recorded. In the form there will be a combo that will use the Brand class, only to filter the next combo (the models of the cars).


Forms.py

import json
from django import forms
from .models import *

class RegCarForm(forms.ModelForm):

    dcars = {}
    list_cars = []
    for car in Car.objects.all():
        if car.brand.company_name in dcars:
            dcars[car.brand.company_name].append(car.name)
        else:
            dcars[car.brand.company_name] = [car.name]
        list_cars.append((car.name,car.name))

    brands = [str(brand) for brand in Brand.objects.all()]

    brand_select = forms.ChoiceField(choices=([(brand, brand) for brand in brands]))
    car_select = forms.ChoiceField(choices=(list_cars))

    brands = json.dumps(brands)
    cars = json.dumps(dcars)

    class Meta:
        model = Fleet
        fields = ('brand_select', 'car_select', 'description',)

Regcarform is the Form for the registration of cars, in it there are 3 fields: brand_select, car_select, and Description, I have created 2 JSON attributes: 1) A dictionary whose keys are the marks and values are lists of their respective expensive models, and 2) A list of brands. These two attributes will serve as auxiliary variables for JS functions.


views

from django.shortcuts import render
from .forms import RegCarForm
from .models import *

# Create your views here.

def regcar(request):
    if request.method == 'POST':
        car_form = RegCarForm(data=request.POST)

        if car_form.is_valid():
            cdata = car_form.cleaned_data.get
            car_selected = Car.objects.filter(name=cdata('car_select'))
            reg1 = Fleet(car_id=car_selected[0].id, description=cdata('description'))
            reg1.save()
        else:
            print ('Invalid')

    else:
        car_form = RegCarForm()
    return render(request, 'core/regcar.html', {'car_form': car_form})

The view is self-explanatory, assigns the Form to the car_form variable, renders the core template/regcar.html and, after the Post, validates the form and saves the data.


regcar.html (Django template)

{% extends "base.html" %}

{% block head %}
{% endblock %}

{% block content %}
    <h1>Registering cars on the fleet. <br />(Populate one drop down based on selection in another)</h1>
    <p>Change the contents of drop down Car based on the selection in dropdown Brand, using Django-forms + Javascritp</p>
    <div class="select-style">
        <form action="." method="post">
            {% csrf_token %}
            {{ car_form.as_p }}
            <p><input type="submit" value="Register a car"></p>
        </form>
    </div>
{% endblock %}

{% block js %}
    {% include "js1.html" %}
{% endblock %}

The template only renders the form and loads the JS script.


Finally, the JS script. What does the heavy lifting.

{% block js %}
    <script language="javascript">
        $('#id_brand_select').change(function() {populateCar(this)});
        $('#id_description').addClass('descriptions');
        cars = {{ car_form.cars | safe }}
        brands = {{ car_form.brands | safe}};
        populateBrand();
        $("#id_car_select").empty();
        $("#id_car_select").append('<option value="" disabled selected>First select a brand</option>');

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

        function populateCar(event) {
            brand = $("#id_brand_select option:selected").text();
            $("#id_car_select").empty();
            $("#id_car_select").append('<option value="" disabled selected>Select your option</option>');
            for (let [b, bcars] of Object.entries(cars)) {
                if (b == brand) {
                    //alert(b);
                    for (car in bcars) {
                        $('#id_car_select')
                            .append($("<option></option>")
                                .attr("value", bcars[car])
                                .text(bcars[car]));
                    }
                }
            }
        }
    </script>
{% endblock %}

When the document is loaded, the script assigns the event change of the brand_select element (combo for tag selection) to the populateCar function, creates two JS variavei by assigning the values of the form’s JSON attributes to them (Cars and Brands) and calls the function populateBrand.

Links:

Complete project in Django:
https://github.com/Sidon/djfkf/

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

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

This Question in English

Browser other questions tagged

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