使用动态数量的字段创建Django表单类

时间:2011-03-29 20:20:01

标签: python django forms

我正在做类似网上商店的事情。我正在制作一个客户购买物品的表格,她可以选择她想购买的这些物品中有多少。但是,在她购买的每件商品上,她都需要选择它的颜色。因此,有一个非常数量的字段:如果客户购买3个项目,她应该获得3个<select>框用于选择颜色,如果她购买7个项目,她应该得到7个这样的<select>框。

我将使用JavaScript使HTML表单字段显示和消失。但是如何在我的Django表单类中处理这个问题呢?我看到表单字段是类属性,所以我不知道如何处理某个表单实例应该有3个颜色字段和7个字段的事实。

有任何线索吗?

5 个答案:

答案 0 :(得分:68)

Jacob Kaplan-Moss在动态表单字段上有大量的文章: http://jacobian.org/writing/dynamic-form-generation/

实际上,您在实例化期间向表单的self.fields字典添加了更多项。

答案 1 :(得分:31)

这是另一种选择:formset怎么样? 由于您的字段都是相同的,这正是用于表单集的。

django管理员使用FormSet s +一些javascript来添加任意长度的内联。

class ColorForm(forms.Form):
    color = forms.ChoiceField(choices=(('blue', 'Blue'), ('red', 'Red')))

ColorFormSet = formset_factory(ColorForm, extra=0) 
# we'll dynamically create the elements, no need for any forms

def myview(request):
    if request.method == "POST":
        formset = ColorFormSet(request.POST)
        for form in formset.forms:
            print "You've picked {0}".format(form.cleaned_data['color'])
    else:
        formset = ColorFormSet()
    return render(request, 'template', {'formset': formset}))

的JavaScript

    <script>
        $(function() {
            // this is on click event just to demo.
            // You would probably run this at page load or quantity change.
            $("#generate_forms").click(function() {
                // update total form count
                quantity = $("[name=quantity]").val();
                $("[name=form-TOTAL_FORMS]").val(quantity);  

                // copy the template and replace prefixes with the correct index
                for (i=0;i<quantity;i++) {
                    // Note: Must use global replace here
                    html = $("#form_template").clone().html().replace(/__prefix_/g', i);
                    $("#forms").append(html);
                };
            })
        })
    </script>

模板

    <form method="post">
        {{ formset.management_form }}
        <div style="display:none;" id="form_template">
            {{ formset.empty_form.as_p }}
        </div><!-- stores empty form for javascript -->
        <div id="forms"></div><!-- where the generated forms go -->
    </form>
    <input type="text" name="quantity" value="6" />
    <input type="submit" id="generate_forms" value="Generate Forms" />

答案 2 :(得分:19)

你可以像

那样做
def __init__(self, n,  *args, **kwargs):
  super(your_form, self).__init__(*args, **kwargs)
  for i in range(0, n):
    self.fields["field_name %d" % i] = forms.CharField()

当您创建表单实例时,只需执行

forms = your_form(n)

这只是基本的想法,您可以将代码更改为您想要的任何内容。 :d

答案 3 :(得分:3)

我这样做的方法如下:

  1. 创建&#34;空&#34;继承自froms.Form的类,如下所示:

    class ItemsForm(forms.Form):
        pass
    
  2. 构造作为实际表单的表单对象的字典,其组成将取决于上下文(例如,您可以从外部模块导入它们)。例如:

    new_fields = {
        'milk'  : forms.IntegerField(),
        'butter': forms.IntegerField(),
        'honey' : forms.IntegerField(),
        'eggs'  : forms.IntegerField()}
    
  3. 在视图中,您可以使用python native&#34; type&#34;函数动态生成具有可变字段数的Form类。

    DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
    
  4. 将内容传递给表单并在模板中呈现:

    Form = DynamicItemsForm(content)
    context['my_form'] = Form
    return render(request, "demo/dynamic.html", context)
    
  5. &#34;内容&#34;是一个字段值的字典(例如甚至request.POST会这样做)。 您可以看到我的整个示例已解释为here

答案 4 :(得分:0)

另一种方法:我们可以使用mixin覆盖字段,而不是破坏常规的字段初始化流程,而是在generate_dynamic_fields中返回动态字段的OrderedDict,并在设置该字段时将其添加。

from collections import OrderedDict

class DynamicFormMixin:
    _fields: OrderedDict = None

    @property
    def fields(self):
      return self._fields

    @fields.setter
    def fields(self, value):
        self._fields = value
        self._fields.update(self.generate_dynamic_fields())

    def generate_dynamic_fields(self):
        return OrderedDict()

一个简单的例子:

class ExampleForm(DynamicFormMixin, forms.Form):
    instance = None

    def __init__(self, instance = None, data=None, files=None, auto_id='id_%s', prefix=None, initial=None,
                 error_class=ErrorList, label_suffix=None, empty_permitted=False, field_order=None,
                 use_required_attribute=None, renderer=None):
        self.instance = instance
        super().__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, field_order,
                         use_required_attribute, renderer)

    def generate_dynamic_fields(self):
        dynamic_fields = OrderedDict()
        instance = self.instance
        dynamic_fields["dynamic_choices"] = forms.ChoiceField(label=_("Number of choices"),
                                                              choices=[(str(x), str(x)) for x in range(1, instance.number_of_choices + 1)],
                                                              initial=instance.initial_choice)
        return dynamic_fields