我理解override the default queryset 'used' by the modelformset可以。这只是限制了创建表单的对象。
我还发现了一个关于filtering ForeignKey choices in a Django ModelForm的Stack Overflow问题,但没有找到ModelForm Set 和limiting available choices in a Django formset,而不是 Model FormSet。我在下面提供了此代码的版本。
我想要做的是为一个学校类('teachinggroup'或'theclass'以避免与'class'关键字冲突)呈现一个ModelFormSet,其中一个字段受查询集的限制。这是教师的班级编辑表格,能够将学生重新分配到不同的班级,但仅限于同一班级内的班级。
我的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())
正如您所看到的,我将选择限制在queryset 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的建议阅读了James Bennett的帖子How can I limit the available choices for a foreign key field in a django modelformset?,但这涉及到在forms.py中修改__init__
方法,但这是唯一的方法知道如何使用modelformset_factory创建ModelFormSet,它不涉及在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
答案 0 :(得分:5)
尽我所知,除了一个之外,你实际上把所有的碎片放在一起。这是最后一个链接。
您说过您阅读了动态表单帖子,其中涉及覆盖您没有的__init__
子类中的forms.Form
方法。但是,没有什么可以阻止你拥有一个,这就是你可以覆盖你的选择。
尽管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
只接受该类,这意味着将在不带参数的情况下调用构造函数。如果需要动态发送参数,那么执行此操作的方法是创建一个生成 class 形式的元类,并在modelformset_factory
调用中调用该元类。
答案 1 :(得分:1)
您可以通过在表单集中设置表单的字段选项,其格式为 init 并覆盖self.fields [&#39; field_name&#39;]。的选项。这对我来说很好,但是在初始化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循环似乎很好地结合在一起:))