如何循环表单字段选项并显示关联的模型实例字段

时间:2010-12-14 08:55:15

标签: python django django-templates django-forms

我有一个带有多选字段的ModelForm。选择是属于特定俱乐部的徒步旅行者的填充实例。

我想通过在第1列包含复选框的表格中显示选项来自定义表单显示的方式,还有一些列显示每个hiker的详细信息。例如,列是(checboxes,名称,年龄,最喜欢的远足径)。

我不确定如何处理这个问题。如何使用模板中的关联模型实例字段访问和显示表单字段选项。有人知道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 个答案:

答案 0 :(得分:37)

如果您在HTML模板中定义整个表单,则最简单。您应该能够在模板中迭代字段的值,如下所示:

{% for value, text in form.hikers.field.choices %}
    {{ value }}: {{ text }}
{% endfor %}

答案 1 :(得分:10)

试试这个解决方案:

<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/

答案 2 :(得分:8)

这非常棘手,但您可以使用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}

答案 3 :(得分:1)

我认为,您可以使用自己的render()方法定义从CheckboxSelectMultiple继承的自己的widget类,并自定义html输出。 See source code, line 690

它也可以在任何模板中重复使用。

答案 4 :(得分:1)

这个答案提供了一个自定义表单小部件 - TableSelectMultiple - 听起来像你想要的那样:

还有original Django Snippet

答案 5 :(得分:1)

相关票证:https://code.djangoproject.com/ticket/9230

我制作了一个制作这样一张桌子的小部件:http://skyl.org/log/post/skyl/2011/01/wherein-the-inner-workings-of-the-deathstarwidget-are-revealed/

我在这里寻找更好的解决方案:D

答案 6 :(得分:0)

通用解决方案的另一个示例:

{% for widget in form.field_name %}
<tr>
    <th>
        <label for="{{widget.id_for_label}}">
            <input type="{{widget.data['type']}}" name="{{widget.data['name']}}" value="{{widget.data['value']}}" {% if widget.data['selected'] %}selected{% endif %} {% for k, v in widget.data['attrs'].items() %} {{k}}="{{v}}" {% endfor %}>
        </label>
    </th>
    <td>
        {{widget.choice_label}}
    </td>
</tr>
{% endfor %}

说明:

基本上,您只需遍历form.field_name,然后您会得到一个像这样的小部件:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__html__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'choice_label', 'data', 'id_for_label', 'parent_widget', 'renderer', 'tag', 'template_name'] ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__html__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'choice_label', 'data', 'id_for_label', 'parent_widget', 'renderer', 'tag', 'template_name'] 

其中widget.data包含构建输入元素所需的所有信息:

{'name': 'field_name', 'value': 1, 'label': 'Field name 1', 'selected': False, 'index': '0', 'attrs': {'id': 'id_field_name_0'}, 'type': 'checkbox', 'template_name': 'django/forms/widgets/checkbox_option.html'} 

答案 7 :(得分:0)

也许可以帮助某人。

template.html

<!-- radio -->
<div class="form-group">
    {{ form.field_name.label_tag }}
    {% for pk, choice in form.field_name.field.widget.choices %}
    <div class="custom-control custom-radio custom-control-inline">
        <input id="id_{{form.field_name.name}}_{{ forloop.counter0 }}" name="{{form.field_name.name}}" type="{{form.field_name.field.widget.input_type}}" value="{{pk}}" class="custom-control-input"
         {% ifequal form.field_name.data pk.0 %}
           checked="checked"
         {% endifequal %}/>
        <label for="id_{{form.field_name.name}}_{{ forloop.counter0 }}" class="custom-control-label">{{ choice }}</label>
    </div>
    {% endfor %}
</div>

<!-- checkbox -->
<div class="form-group">
    {{ form.field_name.label_tag }}
    {% for pk, choice in form.field_name.field.widget.choices %}
    <div class="custom-control custom-checkbox custom-control-inline">
        <input id="id_{{form.field_name.name}}_{{ forloop.counter0 }}" name="{{form.field_name.name}}" type="{{form.field_name.field.widget.input_type}}" value="{{pk}}" class="custom-control-input"
         {% ifequal form.field_name.data pk.0 %}
           checked="checked"
         {% endifequal %}/>
        <label for="id_{{form.field_name.name}}_{{ forloop.counter0 }}" class="custom-control-label">{{ choice }}</label>
    </div>
    {% endfor %}
</div>

How to custom checkbox and radio in Django using Bootstrap

My result