2012-11-14 24 views
2

我正在顯示一個modelformset,我希望表單按其某個字段的內容排序。所以我想在模板中使用相當於SomeModel.objects.filter(whatever).order_by('somefield')的模型(模型)。更改表單中表單的顯示順序

我怎樣才能做到這一點?

請注意,can_order不會做我想要的(它必須是自動的,而不是用戶指定的)。我也嘗試過其他的東西,比如dictsort過濾器,但是它會產生不可預測的輸出(即不是由指定的字段排序)。

我甚至嘗試過{% regroup formset by somefield as sorted_formset %},但生成的sorted_formset不能用作(迭代)作爲一個正常的formset。

+2

https://docs.djangoproject.com/en/1.4/topics/forms/modelforms/#changing-the-queryset幾乎可以準確顯示出您的需求。只需將你的有序查詢作爲'queryset'的參數[ – rantanplan

+4

]在[此修補程序](https://github.com/django/django/commit/680268a683):-)中獲得了我的名字在Django項目中。 –

回答

2

由於rantanplan的評論我找到了解決辦法。我無法使用上面鏈接中描述的方法,因爲我不知道queryset將會是什麼(這是一個嵌套formset的複雜形式)。

無論如何,我通過覆蓋來自Django的BaseInlineFormSet類的get_queryset方法找到了解決方案。 我在下面複製了它,包括我的mod,以防谷歌搜索有幫助。

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 
+1

@ little_birdie的答案在下面更好。只需使用Python繼承來修改從超級方法返回的查詢集。 – Bobort

2

感謝@約翰 - 彼得斯上面的回答,爲我指出了正確的方向。但這裏是一個更好的辦法:

MyFormset(inlineformset_factory(...)): 

    def get_queryset(self): 
    return super(MyFormset, self).get_queryset().order_by('myfieldname') 

這樣你就不必Django的代碼複製或混亂,並可能導致破損的道路..只是採取的Django給你查詢集並覆蓋排序。我在我自己的代碼中使用了它,它工作。

編輯。在完成這一步之後,我意識到雖然它看起來工作正常,但它以某種方式混淆了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(),這大大加快了我的大型模型集!

+0

如果您的查詢集處理M2M關係,您可能會收到「重複項」。在這種情況下,只需在查詢集的結尾處彈出一個'.distinct()'。 – Bobort

3

,如果你沒有定義表單集,這就是「內嵌代碼」版本:

FS=inlineformset_factory(ParentClass,ChildClass) 
formset=FS(instance=parentobject, 
      queryset=parentobject.childobject_set.order_by("-time_begin") 
     ) 
0

要完成的答案。有兩種方法可以控制formset中的表單順序:formsets尊重給定queryset的順序(這在其他答覆中顯示)。另一種方式(如果你想擁有的形式完全受到控制的順序)定義自定義表單集類並覆蓋__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的文檔中描述:

對錶單集的迭代將按照創建的順序呈現表單。您可以通過爲__iter__()方法提供備用 實施來更改此訂單。

表單集也可以被索引到,它返回相應的 表單。如果您覆蓋__iter__,則還需要覆蓋 __getitem__以具有匹配的行爲。

https://docs.djangoproject.com/en/dev/topics/forms/formsets/#django.forms.formsets.BaseFormSet

實現這些方法的例子是例如在該SO螺紋:modelformset __iter__ overloading problem