【问题标题】:Formsets with checkboxes带有复选框的表单集
【发布时间】:2011-05-12 05:43:58
【问题描述】:

我希望能够在页面上显示模型列表,并允许用户同时选择其中的几个。

例如,假设我有一个用户选择屏幕,用于模型:

class User(model):
   first = # char field
   last  = # char field
   birthdate = # date

然后我想向用户展示并让他们选择其中一个:

Please select users:
[] John  Smith   Jan, 2001
[] Mike  Davis   Feb, 2002
[] John  Doe     Dec, 2000

[Continue]

然后此表单将被 POST 和处理。

我能想到的一种方法是使用 ModelFormset。 问题是当我尝试使用 ModelFormsets 显示用户时,我无法添加复选框。

我能想到的另一种方法是创建一个表单,并在其上输出一大堆具有特定 ID 的复选框。然后在提交时 - 遍历所有选定的复选框。不确定这将如何与 Django 的表单一起使用。

欢迎提出任何建议。谢谢。

编辑:事实证明,通过在同一名称组中为每个复选框提供一个 ID(例如患者 ID)并简单地查看 django 视图中的 POST 字典,这正是我所需要的!

【问题讨论】:

标签: django django-forms


【解决方案1】:

我创建了一个自定义 SelectMultiple 小部件,用于显示对象详细信息以及一个复选框,用于在表单仍称为新表单时进行选择 - 它似乎仍然有效:

from django.forms import CheckboxInput, SelectMultiple
from django.utils.encoding import force_unicode
from django.utils.html import escape
from django.utils.safestring import mark_safe

class TableSelectMultiple(SelectMultiple):
    """
    Provides selection of items via checkboxes, with a table row
    being rendered for each item, the first cell in which contains the
    checkbox.

    When providing choices for this field, give the item as the second
    item in all choice tuples. For example, where you might have
    previously used::

        field.choices = [(item.id, item.name) for item in item_list]

    ...you should use::

        field.choices = [(item.id, item) for item in item_list]
    """
    def __init__(self, item_attrs, *args, **kwargs):
        """
        item_attrs
            Defines the attributes of each item which will be displayed
            as a column in each table row, in the order given.

            Any callables in item_attrs will be called with the item to be
            displayed as the sole parameter.

            Any callable attribute names specified will be called and have
            their return value used for display.

            All attribute values will be escaped.
        """
        super(TableSelectMultiple, self).__init__(*args, **kwargs)
        self.item_attrs = item_attrs

    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = []
        has_id = attrs and 'id' in attrs
        final_attrs = self.build_attrs(attrs, name=name)
        output = []
        str_values = set([force_unicode(v) for v in value]) # Normalize to strings.
        for i, (option_value, item) in enumerate(self.choices):
            # If an ID attribute was given, add a numeric index as a suffix,
            # so that the checkboxes don't all have the same ID attribute.
            if has_id:
                final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
            cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
            option_value = force_unicode(option_value)
            rendered_cb = cb.render(name, option_value)
            output.append(u'<tr><td>%s</td>' % rendered_cb)
            for attr in self.item_attrs:
                if callable(attr):
                    content = attr(item)
                elif callable(getattr(item, attr)):
                    content = getattr(item, attr)()
                else:
                    content = getattr(item, attr)
                output.append(u'<td>%s</td>' % escape(content))
            output.append(u'</tr>')
        return mark_safe(u'\n'.join(output))

示例形式:

class JobSelectionForm(forms.Form):
    jobs = forms.MultipleChoiceField(widget=TableSelectMultiple(
               item_attrs=('formatted_number', 'name', 'client', 'get_status_display')))

    def __init__(self, accessible_jobs, *args, **kwargs):
        super(JobSelectionForm, self).__init__(*args, **kwargs)
        self.fields['jobs'].choices = [(j.id, j) \
                                       for j in accessible_jobs]

使用上述表单,您可以在实例化表单对象时将要显示的项目列表作为第一个参数传递。

模板:

{% if form.jobs.errors %}{{ form.jobs.errors }}{% endif %}
<table>
<thead>
  <tr>
    <th>&nbsp;</th>
    <th>Number</th>
    <th>Name</th>
    <th>Client</th>
    <th>Status</th>
  </tr>
</thead>
<tbody>
{{ form.jobs }}
</tbody>
</table>

【讨论】:

  • 谢谢,这看起来很有趣。但是当我回复 Daneil 的回答时,我觉得在我的 python 代码中混合 html 真的很不舒服。
  • 在这种情况下,应该可以从自定义小部件生成 (rendered_input_field, related_object) 的元组,但它也涉及创建自定义 BoundField(这就是你当您执行{{ form.fieldname }}) 时获取正确使用小部件而不是调用其渲染方法。
【解决方案2】:

您不需要表单集,因为它用于编辑一组模型实例中的数据。您需要的是带有 ModelChoiceFieldModelMultipleChoiceField 的单个表单 - 您可能需要更改小部件以使用 CheckboxSelectMultiple

【讨论】:

  • 问题在于 - 每个模型只显示一个标签。而我希望页面显示一个包含“名字、姓氏、出生日期”等列的表格......而 MultipleModelChoideField 每个模型只有一个“标签”。除非我混淆了什么?
  • @drozzy:您可以使用文档中描述的label_from_instance() 函数覆盖(多个)ModelChoiceField 显示的内容。不过,要将这些数据放入表中,您需要一些自定义 JS 处理。
  • 我并不是很想开始在我的 python 代码中添加一些 html。好吧,无论如何感谢您的建议。
  • django 小部件是 python 代码中的 html,无论好坏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-18
  • 1970-01-01
  • 2014-08-12
相关资源
最近更新 更多