Django chained selects with mootools

django forms mootools tips


A due specification here!
Such method works, but IMHO should not be used with a large amount of data, since it uses a js mapping array, but is simple and requires a few lines of code, so why not to use it when we have a controlled set of data?


This is just yet another method to implement chained (cascading) selects in django forms. I'm focusing here on the frontend form construction, not the admin area. If you're interested in the admin area instead, please take a look here.


Imagine we have such models:

class City(models.Model):
  name = models.CharField('name')

class Installation(models.Model):
  name = models.CharField('name')
  city = models.ForeignKey(City)

and imagine we need to create a form where the user can select a city or an installation, but if he selects a city then only the associated installations are available. We can solve the problem as described in the next section.


In the view we create a dictionary which maps the city/installation association:

def myview(request):
  all_installation = Installation.objects.all()
  installation_city_map = {}
  for i in all_installation:
    installation_city_map[int(i.id)] = int(i.commune.id)
  # construct and process the form object...
  return render_to_response('view.html', {'form': form, 'installation_city_map':
installation_city_map}, context_instance=RequestContext(request))

Ok, in the template we have to add a javascript which creates an array from the installation_city_map dictionary, as follows:

  var map = [];
  {% for m in installation_city_map %}
    map[{{ m }}] = {{ installation_city_map|get_item:m }};
  {% endfor %}
<!-- Here comes the form -->

Here we need 2 things:

  • mootools loaded in the page
  • the custom template filter get_item. Its implementation it's quite simple: @register.filter
    def get_item(dictionary, key):
      return dictionary.get(key)

Finally we have to use our javascript map array when the onchange event of the city select field is fired, so in the form.py:

class MyForm(forms.Form):
  onchange = "(function() { $$('#id_installation option').each(function(opt) { if($(this).value && opt.value && map[opt.value] != $(this).value) opt.setStyle('display', 'none'); else opt.setStyle('display', 'block'); }.bind(this)) }.bind(this))()"
  city = forms.ModelChoiceField(label='city', queryset=City.objects.all(), required=False, widget=forms.Select(attrs={'onchange':onchange}))
  installation = forms.ModelChoiceField(label='installation', queryset=Installation.objects.all(), required=False)

That's it, no further implementation is required, please comment here for suggestions.

Subscribe to abidibo.net!

If you want to stay up to date with new contents published on this blog, then just enter your email address, and you will receive blog updates! You can set you preferences and decide to receive emails only when articles are posted regarding a precise topic.

I promise, you'll never receive spam or advertising of any kind from this subscription, just content updates.

Subscribe to this blog

Comments are welcome!

blog comments powered by Disqus

Your Smartwatch Loves Tasker!

Your Smartwatch Loves Tasker!

Now available for purchase!