76👍
Jacob Kaplan-Moss has an extensive writeup on dynamic form fields:
http://jacobian.org/writing/dynamic-form-generation/
Essentially, you add more items to the form’s self.fields
dictionary during instantiation.
39👍
Here’s another option: how about a formset?
Since your fields are all the same, that’s precisely what formsets are used for.
The django admin uses FormSet
s + a bit of javascript to add arbitrary length inlines.
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" />
- [Django]-Strings won't be translated in Django using format function available in Python 2.7
- [Django]-How to create a user in Django?
- [Django]-Allowing RabbitMQ-Server Connections
22👍
you can do it like
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()
and when you create form instance, you just do
forms = your_form(n)
it’s just the basic idea, you can change the code to whatever your want. 😀
- [Django]-Django multiprocessing and database connections
- [Django]-How to avoid AppConfig.ready() method running twice in Django
- [Django]-ImageField overwrite image file with same name
11👍
The way I would do it is the following:
-
Create an “empty” class that inherits from
froms.Form
, like this:class ItemsForm(forms.Form): pass
-
Construct a dictionary of forms objects being the actual forms, whose composition would be dependent on the context (e.g. you can import them from an external module). For example:
new_fields = { 'milk' : forms.IntegerField(), 'butter': forms.IntegerField(), 'honey' : forms.IntegerField(), 'eggs' : forms.IntegerField()}
-
In views, you can use python native “type” function to dynamically generate a Form class with variable number of fields.
DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
-
Pass the content to the form and render it in the template:
Form = DynamicItemsForm(content) context['my_form'] = Form return render(request, "demo/dynamic.html", context)
The “content” is a dictionary of field values (e.g. even request.POST would do).
You can see my whole example explained here.
- [Django]-How do I use the built in password reset/change views with my own templates
- [Django]-Mixin common fields between serializers in Django Rest Framework
- [Django]-Serializing a list of objects with django-rest-framework
1👍
Another approach: Rather than breaking the normal field initialization flow, we can override fields with a mixin, return an OrderedDict of dynamic fields in generate_dynamic_fields which will be added whenever its set.
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()
A simple example:
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
- [Django]-Web application monitoring best practices
- [Django]-Django: Query using contains each value in a list
- [Django]-Django/DRF – 405 Method not allowed on DELETE operation