Django ModelAdmin从ModelForm获取查询集

4

我正在使用一个ModelForm,根据模型创建一个表单,以在我的网站上的各个位置使用。该表单具有外键字段,需要根据用户进行过滤。我已经成功地使用以下方法实现了这一点:

class TestForm(ModelForm):
    def __init__(self,user,*args,**kwargs):
        super (TestForm,self ).__init__(*args,**kwargs) # populates the post
        self.fields['controller'].queryset = Controller.objects.filter(user=user)

    class Meta:
        model = Test
        exclude = ['customer']

在我看来,使用以下代码: form = TestForm(user)

这对于我的Django管理外的表单工作得很好,但是我的网站要求模型也可以在Django管理中进行编辑。因此,我根据Django文档中的示例编写了下面的ModelAdmin代码:

class TestAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        if request.user.is_superuser:
            kwargs['form'] = SuTestForm
        else:
            kwargs['form'] = TestForm(request.user)
        return super(TestAdmin, self).get_form(request, obj, **kwargs)

我认为这应该与我的其他表单一样工作,但是django返回了以下错误:invalid literal for int() with base 10: 'TestForm'

经过一番搜索,我找到了这种方法,它将queryset筛选放在ModelAdmin内部:

form = super(TestAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['controller_fk'].queryset = Controller.objects.filter(custid=cust)
return form

这个方法可以完美地运行,但需要我创建多个ModelForm的副本,这似乎不太符合DRY原则。所以,有没有人知道如何将我的ModelForm查询集返回到ModelAdmin表单中?

我不太确定问题出在哪里(您没有提供完整的堆栈跟踪以查看查询何时出错),但是您可以尝试直接从“get_form”方法返回表单实例,看看会发生什么。 - Serafeim
2个回答

2

问题在于,你实际上是在else子句中实例化了你的表单,而另一个子句返回的是类而不是实例。两个分支都需要返回一个类。

不幸的是,在ModelAdmin类中没有简单的挂钩来为表单实例化提供额外的kwargs:它发生在changeform_view方法深处,而要重写该方法比应该更困难。您需要做一些聪明的事情,从get_form返回一个包含用户值的类。


0

虽然这个问题已经很老了,但我想为未来的搜索提供一个替代方案。

您可以使用一个 工厂函数 来捕获 HttpRequest 对象,并在 TestForm 中使用它,然后将 TestForm 类返回到调用的函数中(在这种情况下是 get_form())。这样,您就可以在 TestForm 中访问当前的 request.user 对象并进行相应的筛选:

forms.py

def _test_form_factory(request):
""" Capture request object for use in TestForm, then return the TestForm class. """

    class TestForm(ModelForm):
        def __init__(self, *args, **kwargs):
            super().__init__(*args,**kwargs)
            if request.user.is_superuser:
                return
            self.fields['controller'].queryset = Controller.objects.filter(user=request.user)

        class Meta:
            model = Test
            exclude = ['customer']

    return TestForm

admin.py

def get_form(self, request, obj=None, **kwargs):
    """ Capture request object for use in the form. """
    self.form = _test_form_factory(request)
    return super().get_form(request, obj, **kwargs)

注意: 所有的代码逻辑都可以在TestForm中执行--不需要SuTestForm,假设它所做的只是将controller表单字段的完整查询集返回。即使SuTestForm执行了与类特定的其他逻辑,您也应该发现现在可以将代码迁移到TestForm,因为您可以访问HttpRequest对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接