2011-09-10 49 views
6

據我所知,有可能override the default queryset 'used' by the modelformset。這只是限制了創建表單的對象。Django過濾器ModelFormSet字段選擇...不同於限制Formset的查詢集

我還發現了一個堆棧溢出問題有關filtering ForeignKey choices in a Django ModelForm,但不是的ModelForm limiting available choices in a Django formset,但不是模式表單集中。我已經在下面包含了我的這個代碼版本。

我想要做的是爲一個學校類('teachinggroup'或'theclass'避免與'class'關鍵字衝突)渲染一個ModelFormSet,其中一個字段受queryset限制。這是爲了教師的班級編輯形式,能夠將學生重新分配給不同的班級,但僅限於同一隊列中的班級。

我的models.py

class YearGroup(models.Model): 
    intake_year = models.IntegerField(unique=True) 
    year_group = models.IntegerField(unique=True, default=7) 
    def __unicode__(self): 
     return u'%s (%s intake)' % (self.year_group, self.intake_year) 

    class Meta: 
     ordering = ['year_group'] 

class TeachingGroup(models.Model): 
    year = models.ForeignKey(YearGroup) 
    teachers = models.ManyToManyField(Teacher) 
    name = models.CharField(max_length=10) 
    targetlevel = models.IntegerField() 
    def __unicode__(self): 
     return u'Y%s %s' % (self.year.year_group, self.name) 

    class Meta: 
     ordering = ['year', 'name'] 

我views.py

def edit_pupils(request, teachinggroup): 
    theclass = TeachingGroup.objects.get(name__iexact = teachinggroup) 
    pupils = theclass.pupil_set.all() 

    PupilModelFormSet = modelformset_factory(Pupil) 

    classes_by_year = theclass.year.teachinggroup_set.all() 
    choices = [t for t in classes_by_year] 
# choices = [t.name for t in classes_by_year] #### I also tried this 

    if request.method == 'POST': 
     formset = PupilModelFormSet(request.POST,queryset=pupils) 
     if formset.is_valid(): 
      formset.save() 
      return redirect(display_class_list, teachinggroup = teachinggroup) 
    else: 
     formset = PupilModelFormSet(queryset=pupils) 
     for form in formset: 
      for field in form: 
       if 'Teaching group' == field.label: 
        field.choices = choices 


    return render_to_response('reassign_pupils.html', locals()) 

正如你所看到的,我限制了選擇的查詢集classes_by_year,這是唯一屬於同一類年組。這個查詢集正確顯示,正如你可以在下面呈現的頁面中看到的那樣,但它根本不影響表單字段。

我的模板中選擇小部件

{% for form in formset %} 
<tr> 
    {% for field in form.visible_fields %} 
    <td>   {# Include the hidden fields in the form #} 
     {% if forloop.first %} 
      {% for hidden in form.hidden_fields %} 
      {{ hidden }} 
      {% endfor %} 
     {% endif %} 
     <p><span class="bigtable">{{ field }}</span> 
     {% if field.errors %} 
      <p><div class="alert-message error"> 
      {{field.errors|striptags}}</p> 
      </div> 
     {% endif %} 
    </td> 
    {% endfor %} 
</tr> 
{% endfor %} 
</table> 
<input type="submit" value="Submit changes"></p> 
</form> 
{{ choices }} <!-- included for debugging --> 

頁面呈現的所有教學組(類)可見,但在頁面底部的標籤呈現爲:[<TeachingGroup: Y8 82Ma2>, <TeachingGroup: Y8 82Ma3>],準確地顯示只有兩個班8年

請注意,我還通過詹姆斯·貝內特後So you want a dynamic form閱讀所推薦的How can I limit the available choices for a foreign key field in a django modelformset?,但涉及到修改__init__方法forms.py,然而唯一的辦法我知道如何創建一個ModelFormSet是modelformset_factory,它沒有不涉及在forms.py中定義任何類。

繼續從Luke Sneeringer獲得幫助,這裏是我的新forms.py條目。在閱讀Why do I get an object is not iterable error?之後,我意識到我的一些問題來自於將元組賦予field.choices方法,當它期待字典時。我用.queryset的做法相反,它工作正常:

class PupilForm(forms.ModelForm): 
    def __init__(self, *args, **kwargs): 
     super(PupilForm, self).__init__(*args, **kwargs) 
     thepupil = self.instance 
     classes_by_year = thepupil.teaching_group.year.teachinggroup_set.all() 
     self.fields['teaching_group'].queryset = classes_by_year 

class Meta: 
    model = Pupil 

回答

4

是最好的,我可以告訴你實際上已經把所有的拼在一起,除了一個。這是最後的鏈接。

你說你讀過動態表單文章,其中涉及覆蓋forms.Form子類中的__init__方法,這是你沒有的。但是,沒有任何東西阻止你擁有一個,這就是你可以覆蓋你的選擇的地方。

儘管modelformset_factory不需要明確的Form類(它構造了一個從模型中提供的模型,如果沒有提供),它可以採用一個。使用form關鍵字參數:

PupilModelFormset = modelformset_factory(Pupil, form=PupilForm) 

顯然,這需要定義PupilForm類。我得到你已經知道如何做到這一點的印象,但它應該是這樣的:

from django import forms 

class PupilForm(forms.ModelForm): 
    def __init__(self, *args, **kwargs): 
     super(PupilForm, self).__init__(*args, **kwargs) 
     self.fields['teaching_group'].choices = ______ # code to generate choices here 

    class Meta: 
     model = Pupil 

你可能有最後一個問題是,modelformset_factory只是需要一流的,這意味着構造函數將被用沒有參數。如果您需要動態地發送參數,則執行此操作的方法是創建一個元類,它自己生成,並在您的modelformset_factory調用中調用該元類。

+0

感謝您的詳細首發,但我承認失敗解釋它。如果我用'__init __(self)'和'__init __()'逐字逐句嘗試,我得到'TypeError:__init __()有一個意外的關鍵字參數'auto_id',它由formset工廠生成。如果我嘗試使用'(self,* args,** kwargs)'和'__init __(* args,** kwargs)'(我只是在黑暗中刺穿),我得到DoesNotExist,並且表單似乎不受約束。如果我嘗試使用'super(PupilForm,self.instance,self)'我得到'AttributeError:'PupilForm'對象沒有屬性'instance'。 – nimasmi

+0

哦,我沒有意識到有爭論。你的第二個猜測是對的。我會更詳細地看看它,並通過編輯回覆您。 –

+0

關於論據的主題,從[上述文章](http://www.b-list.org/weblog/2008/nov/09/dynamic-forms/)繼續我也試過: 'def __init __(自我,學生,* args,** kwargs):' 但得到'__init __()至少需要2個非關鍵字參數(給出1)' – nimasmi

1

您可以通過設置表單的字段選項來完成此操作,形式爲init並覆蓋self.fields ['field_name']。choices。這對我來說工作得很好,但在formset初始化後,我需要更多邏輯。下面是我在Django 1.6.5什麼作品:

from django.forms.models import modelformset_factory 
user_choices = [(1, 'something'), (2, 'something_else')] # some basic choices 
PurchaserChoiceFormSet = modelformset_factory(PurchaserChoice, form=PurchaserChoiceForm, extra=5, max_num=5) 
my_formset = PurchaserChoiceFormSet(self.request.POST or None, queryset=worksheet_choices) 

# and now for the magical for loop and override each desired fields choices 
for choice_form in my_formset: 
    choice_form.fields['model'].choices = user_choices 

我沒能找到這個答案,但嘗試過了,它在Django 1.6.5工作。我想到了,因爲formset和for循環似乎一起很好:)