Создание класса формы Django с динамическим числом полей - PullRequest
48 голосов
/ 30 марта 2011

Я работаю над чем-то вроде интернет-магазина.Я делаю форму, в которой покупатель покупает товар, и она может выбрать, сколько из этих товаров она хотела бы купить.Но на каждом предмете, который она покупает, ей нужно выбрать цвет.Таким образом, существует непостоянное количество полей: если клиент покупает 3 товара, он должен получить 3 <select> коробок для выбора цвета, если он покупает 7 товаров, он должен получить 7 таких <select> коробок.

Я заставлю поля формы HTML появляться и исчезать, используя JavaScript.Но как мне справиться с этим в моем классе формы Django?Я вижу, что поля формы являются атрибутами класса, поэтому я не знаю, как справиться с тем фактом, что у некоторого экземпляра формы должно быть 3 цветовых поля, а у некоторых - 7.

Любая подсказка?

Ответы [ 5 ]

69 голосов
/ 30 марта 2011

У Джейкоба Каплана-Мосса имеется обширная запись для полей динамической формы: http://jacobian.org/writing/dynamic-form-generation/

По сути, вы добавляете больше элементов в словарь self.fields формы во время создания экземпляра.

32 голосов
/ 30 марта 2011

Вот еще один вариант: как насчет 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>

Template

    <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" />
19 голосов
/ 30 марта 2011

вы можете сделать это как

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 голосов
/ 28 февраля 2018

Я бы сделал так:

  1. Создайте «пустой» класс, который наследуется от froms.Form, например:

    class ItemsForm(forms.Form):
        pass
    
  2. Создайте словарь объектов форм, являющихся фактическими формами, состав которых будет зависеть от контекста (например, вы можете импортировать их из внешнего модуля).Например:

    new_fields = {
        'milk'  : forms.IntegerField(),
        'butter': forms.IntegerField(),
        'honey' : forms.IntegerField(),
        'eggs'  : forms.IntegerField()}
    
  3. В представлениях вы можете использовать встроенную в Python функцию «type» для динамической генерации класса Form с переменным числом полей.

    DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
    
  4. Передайте содержимое в форму и отобразите его в шаблоне:

    Form = DynamicItemsForm(content)
    context['my_form'] = Form
    return render(request, "demo/dynamic.html", context)
    

«Содержимое» - это словарь значений полей (например, даже запрос).ПОСТ сделал бы).Вы можете увидеть весь мой пример, объясненный здесь .

0 голосов
/ 09 августа 2018

Другой подход: вместо того, чтобы нарушать нормальный поток инициализации поля, мы можем переопределить поля с помощью mixin, вернуть OrderedDict динамических полей в generate_dynamic_fields, который будет добавляться всякий раз, когда он установлен.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...