Ответ 1
У Джейкоба Каплана-Мосса обширная рецензия на динамические поля формы: http://jacobian.org/writing/dynamic-form-generation/
По сути, вы добавляете больше элементов в словарь формы self.fields
во время создания экземпляра.
Я работаю над чем-то вроде интернет-магазина. Я делаю форму, в которой клиент покупает товар, и она может выбрать, сколько из этих предметов она хотела бы купить. Но, по каждому предмету, который она покупает, ей нужно выбрать, какой будет его цвет. Таким образом, существует непостоянное количество полей: если клиент покупает 3 предмета, она должна получить 3 <select>
бокса для выбора цвета, если она покупает 7 предметов, она должна получить 7 таких <select>
.
Я создам поля формы HTML и исчезаю с помощью JavaScript. Но как я могу справиться с этим в классе Django? Я вижу, что поля формы являются атрибутами класса, поэтому я не знаю, как иметь дело с тем фактом, что экземпляр какой-либо формы должен иметь 3 цветовых поля и около 7.
Любая подсказка?
У Джейкоба Каплана-Мосса обширная рецензия на динамические поля формы: http://jacobian.org/writing/dynamic-form-generation/
По сути, вы добавляете больше элементов в словарь формы self.fields
во время создания экземпляра.
Вот еще один вариант: как насчет formset? Поскольку ваши поля все одинаковые, именно для этих форм используются.
Администратор django использует FormSet
+ бит 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}))
<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" />
ты можешь сделать это как
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
Я бы сделал это следующим образом:
Создайте "пустой" класс, который наследуется от froms.Form
, например так:
class ItemsForm(forms.Form):
pass
Создайте словарь объектов форм, являющихся фактическими формами, состав которых будет зависеть от контекста (например, вы можете импортировать их из внешнего модуля). Например:
new_fields = {
'milk' : forms.IntegerField(),
'butter': forms.IntegerField(),
'honey' : forms.IntegerField(),
'eggs' : forms.IntegerField()}
В представлениях вы можете использовать встроенную в Python функцию "type" для динамической генерации класса Form с переменным количеством полей.
DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
Передайте содержимое в форму и отобразите его в шаблоне:
Form = DynamicItemsForm(content)
context['my_form'] = Form
return render(request, "demo/dynamic.html", context)
"Содержимое" - это словарь значений полей (например, даже request.POST). Вы можете увидеть весь мой пример, объясненный здесь.
Другой подход: вместо того, чтобы нарушать нормальный поток инициализации поля, мы можем переопределить поля с помощью 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