How to get Interdependent dropdowns in django using Modelform and jquery?
Asked Answered
P

2

17

I am new to django and jquery. I am working on a django-based app where I have 3 drop downs in a form. 1. Campus 2. School 3. Centre

The hierarchy is Campuses have schools and schools have centres. I want to interlink these drop-downs.

For example, I have got 3 campuses, say Campus1, Campus2, Campus3. If I select Campus1, I should only get to select schools in campus1 and continuing this, if I select School1 then I need to be able to select centres of School1 and all other options should get hidden.

I searched on net and have tried this http://blog.devinterface.com/2011/02/how-to-implement-two-dropdowns-dependent-on-each-other-using-django-and-jquery/ but it doesn't seem to work for me. I have also checked this http://www.javascriptkit.com/script/script2/triplecombo.shtml but since I am using ModelForm to create forms, this doesn't fit to my needs.

Please guide me to do this.

Thanks

Pervert answered 2/1, 2013 at 11:20 Comment(0)
J
29

You might need to use the following technologies:

  • Custom Django Form Fields (Within the model form)
  • ajax(to fetch the records)
  • simplejson(to send a json response to ajax)
  • jquery(to update the combo boxes on client side)

Let's have a look at an example(Not really tested this, just from the top of my mind):

Models.py

from django.db import models

class Campus(models.Model):
    name = models.CharField(max_length=100, choices=choices.CAMPUSES)

    def __unicode__(self):
        return u'%s' % self.name

class School(models.Model):
    name = models.CharField(max_length=100)
    campus = models.ForeignKey(Campus)

    def __unicode__(self):
        return u'%s' % self.name

class Centre(models.Model):
    name = models.CharField(max_length=100)
    school = models.ForeignKey(School)

    def __unicode__(self):
        return u'%s' % self.name

Forms.py

import models
from django import forms

class CenterForm(forms.ModelForm):
    campus = forms.ModelChoiceField(queryset=models.Campus.objects.all())
    school = forms.ModelChoiceField(queryset=models.School.objects.none()) # Need to populate this using jquery
    centre = forms.ModelChoiceField(queryset=models.Centre.objects.none()) # Need to populate this using jquery

    class Meta:
        model = models.Center

        fields = ('campus', 'school', 'centre')

Now, write a method in your views that returns a json object for schools under a campus and centres under a school:

Views.py

import models
import simplejson
from django.http import HttpResponse

def get_schools(request, campus_id):
    campus = models.Campus.objects.get(pk=campus_id)
    schools = models.School.objects.filter(campus=campus)
    school_dict = {}
    for school in schools:
        school_dict[school.id] = school.name
    return HttpResponse(simplejson.dumps(school_dict), mimetype="application/json")

def get_centres(request, school_id):
    school = models.School.objects.get(pk=school_id)
    centres = models.Centre.objects.filter(school=school)
    centre_dict = {}
    for centre in centres:
        centre_dict[centre.id] = centre.name
    return HttpResponse(simplejson.dumps(centre_dict), mimetype="application/json")

Now write a ajax/jquery method to fetch the data and populate the select elements in the HTML.

AJAX/jQuery

$(document).ready(function(){
    $('select[name=campus]').change(function(){
        campus_id = $(this).val();
        request_url = '/get_schools/' + campus_id + '/';
        $.ajax({
            url: request_url,
            success: function(data){
                $.each(data, function(index, text){
                    $('select[name=school]').append(
                         $('<option></option>').val(index).html(text)
                     );
                });
            }
        });
        return false;
    })
});
Jailbird answered 2/1, 2013 at 12:28 Comment(9)
By using ModelChoiceField everywhere you invalidate ModelForm's purpose. Also Select is the default widget anyway, you don't need to declare it. Obviously the Schools are related with Campuses with a Foreign key, based on the OP's description.Communal
I do agree that it may invalidate ModelForm's purpose if the actual code is limited to the above example. Please take into consideration that actual code for Models and Form may differ, above is just an example.Jailbird
Of course it may differ when there is no direct correspondence between the form and the model. Since the OP hasn't explicitly described his models I'll just agree with you about being just an example. I just wanted to state something that might not be immediately obvious to a django newbie.Communal
Either way you deserve a +1 for your complete solution.Communal
I did changes in models.py > CentreForm(ModelForm) as follows: `campus = forms.ModelChoiceField(queryset=Campus.objects.all()) school = forms.ModelChoiceField(queryset={}) centre = forms.ModelChoiceField(queryset={}) class Meta: model = Centre fields = ('campus', 'school', 'centre')'. I am gettingg error as 'AttributeError at / dict' object has no attribute 'all' if I retrieve school/centre field as {{cf.school}}/{{cf.centre}}Pervert
Oops, my bad, should pass models.Model.objects.None instead of an empty {} in the ModelChoiceField where you need to append data on selection, see the updated answer. This will pass in an empty QuerySet object.Jailbird
Thanks for the update. The error is solved. I am getting 3 dropdown boxes and also get_schools() and get_centres() functions in views.py are working properly. Now the jquery is causing a problem. till request_url, it is working properly. something is not working when .ajax starts. Hence the append is not working. Please suggest changes. I am totally new to ajax and json part.Pervert
Where are you using CenterForm?Hipparchus
Edited to fix the ajax call.Pleochroism
R
5

Rather than re-inventing the wheel, I would use Django Smart Selects or Django Autocomplete Light

I haven't tried either yet but I'm about to use one or both of them in an upcoming project.

Rutherford answered 21/10, 2013 at 14:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.