我正在显示一个modelformset,我希望这些表单按其中一个字段的内容排序。所以我想在模板中使用等效的SomeModel.objects.filter(whatever).order_by('somefield')
作为(模型)formset。
我该怎么做?
请注意can_order
不能达到我想要的效果(必须是自动的,而不是用户指定的)。我也尝试过其他的东西,比如dictsort过滤器,但会产生不可预测的输出(即不按指定字段排序)。
我甚至尝试了{% regroup formset by somefield as sorted_formset %}
,但结果sorted_formset
无法作为普通的formset使用(迭代)。
答案 0 :(得分:3)
感谢@ john-peters的回答,指出我正确的方向。但这是一个更好的方法:
MyFormset(inlineformset_factory(...)):
def get_queryset(self):
return super(MyFormset, self).get_queryset().order_by('myfieldname')
这样你就不必复制或搞乱django的代码并可能导致破坏......只需采用django为你提供的查询集并覆盖排序。我在我自己的代码中使用了它,它可以工作。
EDIT。稍微研究一下后,我意识到虽然它看起来工作正常但它在某种程度上弄乱了BaseInlineFormset.get_queryset()中的逻辑,导致重复的数据库查询。但是,希望有人会对此发表评论并予以纠正,我将把它留在这里。同时,我有另一个解决方案,它可以工作,不会导致冗余查询..如下:
MyFormset(inlineformset_factory(...)):
def __init__(self, *args, **kwargs):
super(MyFormset, self).__init__(*args, **kwargs)
self.queryset = self.queryset.order_by('myfieldname')
在完成任何操作之前,它会在安全时修改查询集。在我的代码中,我也在这里做.select_related(),这大大加快了我的大型模型集!
答案 1 :(得分:3)
如果您没有定义Formset,这是“内联代码”版本:
FS=inlineformset_factory(ParentClass,ChildClass)
formset=FS(instance=parentobject,
queryset=parentobject.childobject_set.order_by("-time_begin")
)
答案 2 :(得分:2)
感谢rantanplan的评论我发现了一个解决方案。我无法使用上面链接中描述的方法,因为我不知道查询集将是什么(这是一个具有嵌套表单集的复杂表单)。
无论如何,我通过覆盖Django的BaseInlineFormSet类中的get_queryset方法找到了一个解决方案。 我在下面复制了它,包括我的mod,以防googlers发现它有用。
def get_queryset(self):
'''
Copied this method from Django code and modified the ordering statement
'''
if not hasattr(self, '_queryset'):
if self.queryset is not None:
qs = self.queryset
else:
qs = self.model._default_manager.get_query_set()
# If the queryset isn't already ordered we need to add an
# artificial ordering here to make sure that all formsets
# constructed from this queryset have the same form order.
if not qs.ordered:
# MY MOD IS HERE:
# qs = qs.order_by(self.model._meta.pk.name)
qs = qs.order_by('order_index')
#/MOD
# Removed queryset limiting here. As per discussion re: #13023
# on django-dev, max_num should not prevent existing
# related objects/inlines from being displayed.
self._queryset = qs
return self._queryset
答案 3 :(得分:2)
完成答案。有两种方法可以控制formset中表单的顺序:formsets尊重给定查询集的顺序(这在其他回复中显示)。另一种方法(如果您希望完全控制表单的顺序)是定义自定义formset类并覆盖__iter__
和__getitem__
方法:
from django.forms import BaseModelFormSet
class MyModelBaseFormset(BaseModelFormSet):
def __iter__(self):
"""Yields the forms in the order they should be rendered"""
return ...
def __getitem__(self, index):
"""Returns the form at the given index, based on the rendering order"""
return ...
MyModelFormset = modelformset_factory(model=MyModel, formset=MyModelBaseFormset, queryset=...)
这种方法在Django文档中描述:
迭代formset将按照它们的顺序呈现表单 被创造。您可以通过提供替代方案来更改此订单
__iter__()
方法的实现。Formset也可以索引到,返回相应的 形成。如果您覆盖
__iter__
,则还需要覆盖__getitem__
具有匹配行为。
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#django.forms.formsets.BaseFormSet
实现这些方法的例子就是在这个SO线程中:modelformset __iter__ overloading problem。
答案 4 :(得分:0)
一种更简单的方法,但它可能并不适用于所有情况,但如果您能负担得起,只需在模型元类中定义排序即可。
class ExampleModel(models.Model):
...
class Meta:
ordering = ("name", )
Formset将遵循查询集的顺序,这将遵循Meta类变量的值。