Django ModelAdmin从ModelForm获取查询集

时间:2015-01-21 22:57:27

标签: python django django-models django-forms django-admin

我使用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'

经过一些googeling后,我遇到了这种方法,它将查询集过滤放在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表单中?

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:0)

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

您可以使用工厂函数,该函数将捕获HttpRequest对象以供TestForm使用,然后将TestForm类返回给调用函数(在这种情况下为get_form()。这样,您可以访问request.user中的当前TestForm对象并进行相应的过滤:

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,假设所做的全部是传回{{1 }}中的表单字段。即使controller执行特定于该类的其他逻辑,您也应该发现您现在可以将代码迁移到SuTestForm,因为您可以访问TestForm对象。