2009-08-04 136 views
33

我有一个与另一个对象具有多对多关系的对象。
在Django Admin中,这会在多选框中产生很长的列表。在Django中过滤ManyToMany框管理

我想过滤ManyToMany关系,因此我只能获取客户选择的城市中可用的类别。

这可能吗?我需要为它创建一个小部件吗?如果是这样的话 - 我如何将行为从标准的ManyToMany字段复制到它,因为我也想要filter_horizo​​ntal函数。

这是我的简化模型:

class City(models.Model): 
    name = models.CharField(max_length=200) 


class Category(models.Model): 
    name = models.CharField(max_length=200) 
    available_in = models.ManyToManyField(City) 


class Customer(models.Model): 
    name = models.CharField(max_length=200) 
    city = models.ForeignKey(City) 
    categories = models.ManyToManyField(Category) 

回答

36

好的,这是我使用上述类的解决方案。 我添加了一堆更多的过滤器来正确地过滤它,但我想让代码在这里可读。

这正是我一直在寻找,我在这里找到我的解决方案:http://www.slideshare.net/lincolnloop/customizing-the-django-admin#stats-bottom(幻灯片50)

以下内容添加到我的admin.py:

class CustomerForm(forms.ModelForm): 
    def __init__(self, *args, **kwargs): 
     super(CustomerForm, self).__init__(*args, **kwargs) 
     wtf = Category.objects.filter(pk=self.instance.cat_id); 
     w = self.fields['categories'].widget 
     choices = [] 
     for choice in wtf: 
      choices.append((choice.id, choice.name)) 
     w.choices = choices 


class CustomerAdmin(admin.ModelAdmin): 
    list_per_page = 100 
    ordering = ['submit_date',] # didnt have this one in the example, sorry 
    search_fields = ['name', 'city',] 
    filter_horizontal = ('categories',) 
    form = CustomerForm 

此过滤“类别“列表不删除任何功能! (即:我仍然可以有我心爱的filter_horizo​​ntal :))

ModelForms功能非常强大,我有点惊讶它没有在文档/书中覆盖更多。

0
Category.objects.filter(available_in=cityobject) 

应该这样做。该视图应具有用户选择的城市,无论是在请求中还是作为该视图功能的参数。

+0

但即时通讯谈论django管理员,你是说我应该复制标准视图并添加上面? – schmilblick 2009-08-04 11:24:40

+0

啊,我完全错过了你的问题标题中的整个“Django Admin”部分。我仍然认为这是正确的方法,但我不确定你会把它放在哪里,或者甚至可能。 – AlbertoPL 2009-08-04 11:41:56

1

由于您选择的是同一表单中的客户所在的城市和类别,因此您需要一些JavaScript动态缩小类别选择器,以便仅选择所选城市中的可用类别。

+0

我不觉得热衷于在JavaScript上迭代数以万计的DOM元素,并与另一个巨大的列表进行比较。我会说Javascript绝对不是要走的路,当从数据库中选择类别时,必须完成这一步。 – schmilblick 2009-08-04 11:52:15

12

据我所知,你基本上是想根据一些标准(根据城市的类别)来筛选显示的选择。

您可以使用limit_choices_to属性models.ManyToManyField来完成此操作。所以,改变模型的定义...

class Customer(models.Model): 
    name = models.CharField(max_length=200) 
    city = models.ForeignKey(City) 
    categories = models.ManyToManyField(Category, limit_choices_to = {'available_in': cityId}) 

这应该工作,为limit_choices_to,可用于这个目的。

但有一点需要注意,limit_choices_to在带有自定义中间表的ManyToManyField上使用时不起作用。希望这可以帮助。

+0

这看起来像可以工作!然而......它让我意识到我必须重新建模我的模型:) 在阅读文档时,管理员并不关心limit_choices_to,那么你会怎么做呢? – schmilblick 2009-08-05 06:39:18

+0

我试图按照描述@sim的方式来做同样的事情,但我在/ admin/foo/bar /中得到一个'ValueError'的错误:int()的基数为10:'city'`。有什么我缺少如何实现这种过滤方法吗? – nhinkle 2013-10-13 06:44:27

0

就像Ryan说的那样,必须有一些javascript来根据用户选择的内容动态改变选项。如果保存了城市并重新加载了管理员表单,那么当过滤器工作时,发布的解决方案就可以工作,但考虑用户想要编辑对象然后更改城市下拉菜单但类别中的选项不会刷新的情况。

4

另一种方法是用formfield_for_manytomany在Django Admin中。

class MyModelAdmin(admin.ModelAdmin): 
    def formfield_for_manytomany(self, db_field, request, **kwargs): 
     if db_field.name == "cars": 
      kwargs["queryset"] = Car.objects.filter(owner=request.user) 
     return super(MyModelAdmin, self).formfield_for_manytomany(db_field, request, **kwargs) 

考虑到“汽车”是ManyToMany字段。

检查this link了解更多信息。