首页 文章

Django过滤ModelFormSet字段选择...不同于限制Formset的查询集

提问于
浏览
7

我明白有可能override the default queryset 'used' by the modelformset . 这只是限制了创建表单的对象 .

我还发现了一个关于filtering ForeignKey choices in a Django ModelForm的Stack Overflow问题,但不是ModelForm Setlimiting available choices in a Django formset,而不是 Model FormSet . 我在下面提供了此代码的版本 .

我想做的是为一个学校类('teachinggroup'或'theclass'以避免与'class'关键字冲突)渲染一个ModelFormSet,其中一个字段受查询集的限制 . 这是针对教师的 class 编辑表格,能够将学生重新分配到不同的 class ,但仅限于同一队列中的 class .

我的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())

正如您所看到的,我将选择限制在query_ 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年中的两个类 .

请注意,我按照How can I limit the available choices for a foreign key field in a django modelformset?的推荐发布So you want a dynamic form,但这涉及到在forms.py中修改 __init__ 方法,但我知道如何创建ModelFormSet的唯一方法是使用modelformset_factory,它不涉及在表单中定义任何类的.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

2 回答

  • 5

    据我所知,除了一个之外,你实际上把所有部分放在一起 . 这是最后一个链接 .

    你说你读过动态表单帖子,它涉及覆盖 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 调用中调用该元类 .

  • 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 . 我认为它是因为formsets和for循环似乎很好:)

相关问题