Как зациклить выбор полей формы и отобразить связанные поля экземпляра модели - PullRequest
11 голосов
/ 14 декабря 2010

У меня есть ModelForm с полем множественного выбора.Варианты выбора - это заполненные экземпляры Hikers, принадлежащие к определенному клубу.

Я хочу настроить способ отображения формы, отображая варианты в таблице, где 1-й столбец содержит флажки, и еще несколько столбцов отображаютдетали каждого туриста.Например, столбцы (чекбоксы, имя, возраст, любимая туристическая тропа).

Я не знаю, как к этому подойти.Как мне получить доступ и отобразить варианты выбора полей формы с помощью связанных полей экземпляра модели в моем шаблоне.Кто-нибудь знает, как сделать это в 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 ]

37 голосов
/ 14 декабря 2010

Проще всего будет, если вы определите всю форму в шаблоне HTML. Вы должны иметь возможность перебирать значения поля в шаблоне так:

{% for value, text in form.hikers.field.choices %}
    {{ value }}: {{ text }}
{% endfor %}
10 голосов
/ 05 октября 2015

Попробуйте это решение:

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

8 голосов
/ 18 декабря 2014

Это на удивление сложно, но вы можете сделать это, используя 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}
1 голос
/ 09 августа 2012

связанный билет: https://code.djangoproject.com/ticket/9230

Я создал виджет, который создает такую ​​таблицу: http://skyl.org/log/post/skyl/2011/01/wherein-the-inner-workings-of-the-deathstarwidget-are-revealed/

Я все еще ищу лучшее решение: D

1 голос
/ 14 декабря 2010

Этот ответ предоставляет пользовательский виджет формы - TableSelectMultiple - который звучит так, как вы хотите:

Существует также оригинальный фрагмент Django .

1 голос
/ 14 декабря 2010

Я думаю, вы можете определить собственный класс виджета, унаследованный от CheckboxSelectMultiple, с помощью собственного метода render () и настроить вывод html. См. Исходный код, строка 690

Также будет использоваться в любом шаблоне.

0 голосов
/ 12 апреля 2019

Может быть, помочь кому-то.

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>

Как настроить флажок и радио в Django с помощью Bootstrap

My result

0 голосов
/ 07 декабря 2018

Другой пример общего решения:

{% 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'} 
...