Django 管理员.显示分层下拉过滤器

2022-01-25 00:00:00 python django django-admin

问题描述

我有以下型号:

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;

正如您所见,每家公司都与一个城市相关联,并且正如您所期望的那样,Django 管理员会生成包含城市下拉列表的公司创建表单.但为了改善用户体验,我希望用户首先选择州,然后城市下拉菜单将填充该州的城市.有这样做的标准方法吗?

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?


解决方案

你需要创建一个自定义的widget来选择City模型(即你的模型应该FK to City而不是State),这个widget包含两个Select字段,第一个包含州,第二个在选择州时加载(您需要插入一个视图以根据州 ID 返回城市以填充您的城市选择).

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).

您应该将您的小部件媒体内部类指向链接两个选择的特定 .js 文件.

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

在 ModelAdmin 规范中,将字段的小部件设置为您刚刚创建的自定义小部件,它的媒体将自动添加到 change_form 模板中.

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.

确保您的 .js 文件查找您的常规 JQuery 对象并回退到 django.JQuery,这样您就可以在管理员和整个站点中使用相同的小部件.

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.

相关文章