如何遍历表单字段选项并显示关联模型实例字段

20 投票
8 回答
33535 浏览
提问于 2025-04-16 08:30

我有一个模型表单,其中有一个多选字段。这个字段的选项是属于特定俱乐部的一些登山者的实例。

我想自定义我的表单显示方式,把选项放在一个表格里,第一列是复选框,后面几列显示每个登山者的详细信息。比如,列可以是(复选框、名字、年龄、最喜欢的徒步路线)。

我不太确定该怎么做。我该如何在我的模板中访问和显示表单字段的选项以及它们相关的模型实例字段呢?有没有人知道在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

这其实有点复杂,但你可以通过使用 ModelMultipleChoiceFieldCheckboxSelectMultiple 和一个自定义的模板过滤器来实现。表单和小部件类已经解决了大部分问题,但模板过滤器会根据查询集中的每个实例来决定给你哪个小部件。下面会有详细说明……

通用解决方案

# 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 %}

撰写回答