【问题标题】:django admin - use filter horizontal on a many to many field with intermediate tabledjango admin - 在带有中间表的多对多字段上使用水平过滤器
【发布时间】:2016-01-19 06:21:36
【问题描述】:

是否可以将filter_horizontal 用于带有中间表的ManyToManyField 字段,例如没有中间表的字段?

例如:

class A(models.Model):
    f1 = models.ManyToManyField(B)
    f2 = models.ManyToManyField(C, through='T')

class B(models.Model):
    pass

class C(models.Model):
    pass

class T(models.Model):
    a = models.ForeignKey(A)
    c = models.ForeignKey(C)

class AAdmin(admin.ModelAdmin):
    filter_horizontal = ('f1', 'f2', )

【问题讨论】:

  • 没有错误,也没有小部件显示

标签: django django-admin


【解决方案1】:

如果您的多对多字段带有中间表,则无法显示常规、水平过滤器或垂直过滤器小部件。这样做的原因是中间表可能具有无法在这些小部件中显示的额外字段。

可以将相关模型显示为内联。有关详细信息,请参阅 working with many-to-many intermediary models 上的文档。

【讨论】:

  • 所以即使中间表没有多余的字段也不可能?无论如何,我尝试了文档所说的内容,但它对我不起作用。如果值得我使用 django 1.4.2。
  • 不,这似乎是不可能的,除非你愿意深入研究内部代码,看看发生了什么。在 Django 1.4 中对多对多字段使用内联绝对有效(请参阅the docs)。我不知道为什么它在你的情况下不起作用,因为你没有显示你的代码。
  • @gorus 您链接的 SO 帖子没有帮助,因为 OP 没有使用中间表。您知道如何修改链接的答案吗?
  • @gorus 好的,从您链接的帖子开始,我可以对其进行修改以使其与中间表一起使用。见:stackoverflow.com/questions/44714416
  • 这个Django ticket 中描述的auto_created 技巧可能会有所帮助。
【解决方案2】:

小部件的名称是 FilteredSelectMultiple,您必须在管理视图中覆盖 formfield_for_manytomany 方法。这是我用过的。 我有一个 Company 模型,其中包含多个课程字段,其中 Member 作为中间模型。

class Company(models.Model):
    courses = models.ManyToManyField('courses.Course', through='Member', related_name='companies')

class Member(models.Model):
    user = models.OneToOneField(get_user_model(),
    related_name="%(app_label)s_%(class)s",
    on_delete=models.CASCADE,
)
organization = TenantForeignKey(
    Company, related_name="organization_users", on_delete=models.CASCADE)

这是我的管理员视图

class CompanyAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        db = kwargs.get('using')

        if db_field.name == 'courses':
            kwargs['widget'] = FilteredSelectMultiple(
                db_field.verbose_name, is_stacked=False
            )
        else:
            return super().formfield_for_manytomany(db_field, request, **kwargs)
        if 'queryset' not in kwargs:
            queryset = Course.objects.filter(is_active=True)
            if queryset is not None:
                kwargs['queryset'] = queryset
        form_field = db_field.formfield(**kwargs)
        msg = 'Hold down “Control”, or “Command” on a Mac, to select more than one.'
        help_text = form_field.help_text
        form_field.help_text = (
            format_lazy('{} {}', help_text, msg) if help_text else msg
        )
        return form_field

【讨论】:

  • 这很好用。我会做一些调整。例如,您不需要在覆盖方法的第一行设置db,因为该变量不会再次使用;我会改变代码分支的结构。基本上,Django 自己的formfield_for_manytomany() 方法在检测到中间表时返回None,因此您覆盖该方法并通过直接调用db_field.formfield() 强制它返回表单字段。我认为您可以在该表单字段上设置查询集,因此您不必事先将其放入kwargs,但从根本上说,这个答案很好。
猜你喜欢
  • 2017-01-01
  • 2018-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-26
  • 2016-12-13
  • 2016-06-22
  • 2020-03-20
相关资源
最近更新 更多