如何遍历表单字段选项并显示关联模型实例字段
我有一个模型表单,其中有一个多选字段。这个字段的选项是属于特定俱乐部的一些登山者的实例。
我想自定义我的表单显示方式,把选项放在一个表格里,第一列是复选框,后面几列显示每个登山者的详细信息。比如,列可以是(复选框、名字、年龄、最喜欢的徒步路线)。
我不太确定该怎么做。我该如何在我的模板中访问和显示表单字段的选项以及它们相关的模型实例字段呢?有没有人知道在Django中怎么实现这个?
#models.py
class Club(models.Model):
title = models.CharField()
hikers = models.ManyToManyField(Hikers)
class Hiker(models.Model):
name = models.CharField()
age = models.PositiveIntegerField()
favourite_trail = models.CharField()
#forms.py
class ClubForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
club_pk = kwargs['club_pk']
del kwargs['club_pk']
super(ClubForm, self).__init__(*args, **kwargs)
choices = [(ts.pk, ts.name) for hiker in Club.objects.filter(pk=club_pk)]
self.fields['hikers'].choices = choices
class Meta:
model = Club
fields = ('hikers',)
widgets = {'hikers': forms.CheckboxSelectMultiple}
8 个回答
9
这其实有点复杂,但你可以通过使用 ModelMultipleChoiceField
、CheckboxSelectMultiple
和一个自定义的模板过滤器来实现。表单和小部件类已经解决了大部分问题,但模板过滤器会根据查询集中的每个实例来决定给你哪个小部件。下面会有详细说明……
通用解决方案
# forms.py
from django import forms
from .models import MyModel
class MyForm(forms.Form):
my_models = forms.ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple,
queryset=None)
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['my_models'].queryset = MyModel.objects.all()
# myapp/templatetags/myapp.py
from django import template
from copy import copy
register = template.Library()
@register.filter
def instances_and_widgets(bound_field):
"""Returns a list of two-tuples of instances and widgets, designed to
be used with ModelMultipleChoiceField and CheckboxSelectMultiple widgets.
Allows templates to loop over a multiple checkbox field and display the
related model instance, such as for a table with checkboxes.
Usage:
{% for instance, widget in form.my_field_name|instances_and_widgets %}
<p>{{ instance }}: {{ widget }}</p>
{% endfor %}
"""
instance_widgets = []
index = 0
for instance in bound_field.field.queryset.all():
widget = copy(bound_field[index])
# Hide the choice label so it just renders as a checkbox
widget.choice_label = ''
instance_widgets.append((instance, widget))
index += 1
return instance_widgets
# template.html
{% load myapp %}
<form method='post'>
{% csrf_token %}
<table>
{% for instance, widget in form.job_applications|instances_and_widgets %}
<tr>
<td>{{ instance.pk }}, {{ instance }}</td>
<td>{{ widget }}</td>
</tr>
{% endfor %}
</table>
<button type='submit' name='submit'>Submit</button>
</form>
针对你的具体情况
如果你这样调整表单,它应该就能正常工作:
class ClubForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
cluk_pk = kwargs.pop('club_pk')
super(ClubForm, self).__init__(*args, **kwargs)
self.fields['hikers'].queryset = Club.objects.filter(pk=club_pk)
class Meta:
model = Club
fields = ('hikers',)
widgets = {'hikers': forms.CheckboxSelectMultiple}
14
试试这个解决方案:
<ul>
{% for choice in form.my_choice_field.field.choices %}
<li>
<input type="radio" name="my_choice_field" value="{{choice.0}}"
{% ifequal form.my_choice_field.data choice.0 %}
checked="checked"
{% endifequal %}/>
<label for="">{{choice.1}}</label>
</li>
{% endfor %}
</ul>
查看这个链接: http://www.ilian.io/django-forms-choicefield-and-custom-html-output/
50
最简单的方法是把整个表单都放在一个HTML模板里。你可以在这个模板中像这样循环显示某个字段的值:
{% for value, text in form.hikers.field.choices %}
{{ value }}: {{ text }}
{% endfor %}