更改formset中表单的显示顺序

时间:2012-11-14 21:15:22

标签: python django

我正在显示一个modelformset,我希望这些表单按其中一个字段的内容排序。所以我想在模板中使用等效的SomeModel.objects.filter(whatever).order_by('somefield')作为(模型)formset。

我该怎么做?

请注意can_order不能达到我想要的效果(必须是自动的,而不是用户指定的)。我也尝试过其他的东西,比如dictsort过滤器,但会产生不可预测的输出(即不按指定字段排序)。

我甚至尝试了{% regroup formset by somefield as sorted_formset %},但结果sorted_formset无法作为普通的formset使用(迭代)。

5 个答案:

答案 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类变量的值。