简体   繁体   中英

Django admin. Displaying a hierarchical dropdown filter

I have the following model:

from django.db import models

class State(models.Model):
    name = models.CharField(max_length=30)
    abbreviation = models.CharField(max_length=2)

    def numberOfCities(self):
        return self.city_set.count()

    def __unicode__(self):
        return u"{0} - {1}".format(self.abbreviation, self.name)

class City(models.Model):
    name = models.CharField(max_length=40)
    state = models.ForeignKey(State)

    class Meta:
        verbose_name_plural = 'Cities'

    def __unicode__(self):
        return self.name

class Company(models.Model):
    name = models.CharField(max_length=60)
    description = models.CharField(max_length=1000)
    city = models.ForeignKey(City)

    class Meta:
        verbose_name_plural = 'Companies'

    def __unicode__(self):
        return self.name;

As you can see each company is associated with a city and as you would expect the Django admin generates the company creation form containing a dropdown of cities. But in order to improve the user experience I would like the user to first select the state and then the city dropdown would be populated with cities from that state. Is there a standard way of doing this?

It is not easy to bend the admin, but it is just another django app, albeit a really complex one. I usually do something like this:

  1. Make a custom admin template for the model in question (override the change_form.html).
  2. Make an ajax url that returns the City widget filtered by State id.
  3. Use something like jQuery to replace the City widget when State changes.

Django admin Javascript already makes use of the jQuery library. To avoid conflict with user scripts, Django's jQuery is namespaced as django.jQuery. So you don't have to include a second copy, you can use the django.jQuery object on changelist and add/edit views.

You need to create a custom widget to pick the City model (that is, your model should FK to City and not to State), this widget contains two Select fields, the first one contains the Sstates and the second one gets loaded on the pick of a State (you will need to plug a view to return Cities based on State ID to populate your City select).

You should set your widgets Media inner class point to the specific .js file chaining both Selects.

In the ModelAdmin specification, set your field's widget to the custom widget you just created and it's media will be automatically added to the change_form template.

Make sure that your .js file looks for your regular JQuery object and falls back to django.JQuery, this way you can use this same widget in the admin and through out your site.

(function($) {
// Note that this function works only for one widget per page
$('#state').change(function(){
    $('#city').load('/cities_by_state/', {id: this.value}); // the endpoint returns HTML
});
})(JQuery||django.JQuery);

I've done something similar in an app I use locally for my projects (variable depth up to three levels) and the resulting solution ended up a bit hairy as it had to support multiple widgets per page, dynamic widgets (for inlines), templatetags to render the widget in various forms, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM