【问题标题】:Django ModelAdmin get queryset from ModelFormDjango ModelAdmin 从 ModelForm 获取查询集
【发布时间】:2015-03-20 15:12:25
【问题描述】:

我正在使用 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 内也可以编辑。所以我将这段代码用于我的 ModelAdmin,基于Django Docs

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 表单中吗?

【问题讨论】:

  • 我不太确定问题出在哪里(您还没有提供完整的堆栈跟踪来查看查询出错的地方)但是您可以尝试直接从 get_form 方法返回您的表单实例吗?看看会发生什么?

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


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    您可以使用一个工厂函数,它将捕获TestForm 中使用的HttpRequest 对象,然后将TestForm 类返回给调用函数(在本例中为@987654324 @. 这样就可以访问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 对象。

    【讨论】:

      猜你喜欢
      • 2017-03-25
      • 1970-01-01
      • 2021-12-10
      • 2011-06-18
      • 1970-01-01
      • 2018-01-18
      • 1970-01-01
      • 2013-08-22
      • 2020-01-27
      相关资源
      最近更新 更多