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
对象。
我正在使用 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
将这段代码用于我的 ModelAdminclass 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
对象。