【问题标题】:Django Admin - Filter ManyToManyField with through modelDjango Admin - 通过模型过滤 ManyToManyField
【发布时间】:2019-12-04 03:00:21
【问题描述】:

如何在对象的管理页面中过滤与手动定义的through 模型具有ManyToManyField 关系的查询集?

给定models.py

class Foo(models.Model):
    foo_field1 = models.CharField(max_length=50)

class Main(models.Model):
    main_field1 = models.CharField(max_length=50)
    m2mfield = models.ManyToManyField(Foo, through="FooBar")

class FooBar(models.Model):
    main = models.ForeignKey(Main, on_delete=models.CASCADE)
    foo = models.ForeignKey(Foo, on_delete=models.CASCADE)
    new_field = models.CharField(max_length=50)

在 admin.py 中

class M2MInlineAdmin(admin.TabularInline):
    model = Main.m2mfield.through
    extra = 1

class MainAdmin(admin.ModelAdmin):
   inlines = [M2MInlineAdmin,]
   ...

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        print('called formfield_for_manytomany')
        return super().formfield_for_manytomany(db_field, request, **kwargs)

    def get_field_queryset(self, db, db_field, request):
        print('called get_field_queryset')
        return super().get_field_queryset(db, db_field, request)

我尝试访问这两种方法,但如果我指定through 表,它们都不会被调用。但是,如果 ManyToMany 关系被简单地定义为:

class Main(models.Model):
    main_field1 = models.CharField(max_length=50)
    m2mfield = models.ManyToManyField(Foo)

是否有一种方法可以在指定直通表时过滤查询集(同时能够访问request 上下文)?

编辑:

只有在 modelAdmin 类中没有指定 fieldsets 时,ManyToManyField 指定了 through 模型时才会调用这些方法。

定义fieldsets时如何访问这些方法?

【问题讨论】:

  • 我正在使用 django==2.2.3
  • @ChillarAnand 我想在创建/更改视图中进行过滤。
  • 升级到 2.2.3 并进行测试。在更改/创建视图上,它总是调用 formfield_for_manytomany。无法重现上述场景。 github.com/ChillarAnand/library/blob/master/trash/admin.py 可以粘贴整个模型管理代码吗?上面的 sn-p 是否还缺少其他内容?
  • @ChillarAnand 好吧,我调查了一下,发现确实调用了这些方法,但我指定了fieldsets,并且由于某种原因,如果定义了这个变量,则不会调用这些方法......

标签: python django django-admin manytomanyfield


【解决方案1】:

formfield_for_manytomany 方法 seems to be called 仅在使用默认形式时。当定义字段集时,它使用了不同的形式,这就是没有调用上述方法的原因。

由于您对多对多字段使用表格管理,因此您可以覆盖 get_queryset 以使用字段进行过滤。

class M2MInlineAdmin(admin.TabularInline):
    model = Main.fruits.through
    extra = 1

    def get_queryset(self, request):
        qs = super(M2MInlineAdmin, self).get_queryset(request)
        qs = qs.filter(some_arg=some_value)
        return qs

或者,您可以编写自定义模型表单并在管理员中使用它而不是默认表单。

class MainAdminForm(forms.ModelForm):

    class Meta:
        model = Main
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # custom setup


class MainAdmin(admin.ModelAdmin):
    form = MainAdminForm

【讨论】:

  • 谢谢你,get_queryset 里面的 TabularInline 成功了。
【解决方案2】:

您可以在内联类上使用formfield_for_foreignkey() 方法。

class M2MInlineAdmin(admin.TabularInline):
    model = Main.m2mfield.through
    extra = 1

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

【讨论】:

  • 我需要在执行此过滤时访问请求上下文(object.idrequest.user
  • 我收到inlineformset_factory() got an unexpected keyword argument 'user'
  • @drec4s 您是否将上面的 init 方法添加到自定义表单集中? super() 上面的用户行很重要。它允许您在过滤中使用self.user
  • 是的,完全按照您发布的内容复制,我将self.user 设置在super() 内部__init__ 方法之上
  • 哇,我肯定是想太多了。请参阅上面的更简单的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-13
  • 2020-02-01
  • 2022-11-25
  • 1970-01-01
  • 2014-01-30
相关资源
最近更新 更多