Come implementare due dropdown dipendenti l’una dall’altra in Django e jQuery

By | 25 febbraio 2011

Ciao a tutti.

Con l’articolo di oggi vorrei mostrare come implementare due dropdown dipendenti l’una dall’altra usando Django e jQuery.

Supponiamo di avere una relazione 1-N tra marca di automobile e modello: nella prima dropdown vogliamo mostrare la lista delle marche; una volta selezionata una marca mostreremo nella seconda dropdown la lista dei modelli filtrati per quella marca.

Ipotizziamo di avere i modelli definiti così:

1
2
3
4
5
6
7
8
9
#models.py
class VehicleBrand(models.Model):
    description = models.CharField(max_length=100)
    code = models.SlugField(primary_key=True)

class VehicleModel(models.Model):
    description = models.CharField(max_length=100)
    code = models.SlugField(primary_key=True)
    brand = models.ForeignKey(VehicleBrand)

Iniziamo a definire il templatetag da includere nei nostri template:

1
2
3
4
5
6
7
8
9
10
11
#templatetags.py
from models import VehicleBrand

from django import template

register = template.Library()

@register.inclusion_tag("brand_model_select.html")
def brand_model_select():
    brand_list = VehicleBrand.objects.all()
    return {'brand_list' : brand_list}

Come possiamo vedere, il tag richiede un template html di supporto. Vediamo come è definito:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!-- brand_model_select.html -->
<form action="" method="get" accept-charset="utf-8">
    <select name="brand" id="brand">
        <option value="Z">Select a brand</option>
        {% for brand in brand_list %}
            <option value="{{ brand.code}}">{{ brand.description }}</option>
        {% endfor %}
    </select>
    <select name="model" id="model" disabled="true">
        <option>Select a model</option>
    </select>
</form>
<script>
    $(document).ready(
                     function() {
                         $("select#brand").change(function() {
                             if ($(this).val() == 'Z') {
                                 $("select#model").html("<option>Select a model</option>");
                                 $("select#model").attr('disabled', true);
                             }
                             else {
                                 var url = "/brand/" + $(this).val() + "/all_json_models";
                                 var brand = $(this).val();
                                 $.getJSON(url, function(models) {
                                     var options = '<option value="Z">Select a model</option>';
                                     for (var i = 0; i < models.length; i++) {
                                        options += '<option value="' + models[i].pk + '">' + models[i].fields['description'] + '</option>';
                                     }
                                     $("select#model").html(options);
                                     $("select#model option:first").attr('selected', 'selected');
                                     $("select#model").attr('disabled', false);
                                 });
                             }
                         });


                         $("select#model").change(function(vent) {
                             if ($(this).val() == -1) {
                                 return;
                             }
                             myAwesomeFunctionToCallWhenAModelIsSelected();
                         });
                     });
    }

</script>

Possiamo notare che non appena viene selezionato una marca, viene invocata una chiamata Ajax a questa url:
/brand/”selected_brand_code”/all_json_models

Dobbiamo definire dunque un’apposita view per poter intercettare questa url:

1
2
3
4
5
6
#views.py
def all_json_models(request, brand):
    current_brand = VehicleBrand.objects.get(code=brand)
    models = VehicleModel.objects.all().filter(brand=current_brand)
    json_models = serializers.serialize("json", models)
    return HttpResponse(json_models, mimetype="application/javascript")

e in urls.py:

1
2
3
#urls.py
...
(r'^brand/(?P<brand>[-\w]+)/all_json_models/$', 'all_json_models'),

Infine, il metodo javascript myAwesomeFunctionToCallWhenAModelIsSelected(); verrà scatenato una volta selezionato il nostro modello.

Category: Python Tag:, ,

About Stefano

Stefano Mancini is a co-founder of DevInterface.

After graduating in Computer Science, he first specialized in Java/J2EE development by participating in several international projects in the pharmaceutical and banking ambits.

Enthusiast of agile development, like SCRUM for project management and eXtreme Programming for code writing, he then moved to dynamic languages like Ruby and Python.

10 thoughts on “Come implementare due dropdown dipendenti l’una dall’altra in Django e jQuery

  1. Mario

    Great Post! i just need it!
    I think that there is small bug in views.py:

    Put VehicleBrand.objects.get() instead of VehicleBrand.get() and VehicleModel.objects.all().filter() instead if VehicleModel.all().filter()

  2. Andrea

    Appena usato proprio in un portale per auto usate,

    complimenti per il tuo lavoro

  3. benny

    This is just what I need.
    Anyway do you have a sample project files, I really new with Django. Thanks.

  4. Gaurav

    This did not work for me unfortunately. I did a very similar thing, but I could not get anything populated in the vehicle model drop down menu

  5. Manos

    Thank you, thank you, thank you!! I’ve been trying to do this for 10s of hours.. Your post is exactly what I need and works like a charm!

Comments are closed.