Django ModelAdmin 从 ModelForm 获取查询集

Django ModelAdmin get queryset from ModelForm

我正在使用 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 Admin 之外的表单工作正常,但我的站点要求模型也可以在 Django Admin 中编辑。所以我根据 Django Docs

将这段代码用于我的 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'

经过一些谷歌搜索后,我遇到了这种将查询集过滤放在 ModelAdmin 中的方法:

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

这工作得很好,但它确实需要我创建多个看起来不太枯燥的 ModelForm 副本。所以我想有人知道如何将我的 ModelForm 查询集返回到 ModelAdmin 表单中吗?

问题在于您实际上是在 else 子句中实例化表单,而另一个子句 return 是 class 而不是实例。两个分支都需要 return a class.

不幸的是,ModelAdmin class 中没有简单的钩子来为表单实例化提供额外的 kwargs:它发生在 changeform_view 方法的深处,比它应该的更难覆盖.你需要做一些聪明的事情来 return 一个 class 与从 get_form.

中烘焙的用户值

老问题,但我想我会为以后的搜索提供一个替代方案。

您可以使用 工厂函数,它将捕获 HttpRequest 对象用于 TestForm,然后 return TestForm class 回到调用函数(在本例中,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 执行其他特定于 class 的逻辑,您应该发现您现在可以将代码迁移到 TestForm,因为您可以访问 HttpRequest 对象。