Ich arbeite an so etwas wie einem Online-Shop. Ich mache ein Formular, in dem der Kunde einen Artikel kauft, und sie kann auswählen, wie viele dieser Artikel sie kaufen möchte. Bei jedem Artikel, den sie kauft, muss sie jedoch die Farbe auswählen. Es gibt also eine nicht konstante Anzahl von Feldern: Wenn der Kunde 3 Artikel kauft, sollte er 3 <select>
-Boxen für die Auswahl einer Farbe erhalten, wenn er 7 Artikel kauft, sollte er 7 solcher <select>
-Boxen erhalten.
Ich werde die HTML-Formularfelder mit JavaScript anzeigen und verschwinden lassen. Aber wie gehe ich damit in meinem Django-Formkurs um? Ich sehe, dass Formularfelder Klassenattribute sind, und ich weiß nicht, wie ich damit umgehen soll, dass eine Formularinstanz 3 Farbfelder und einige 7 haben sollte.
Irgendeine Ahnung?
Jacob Kaplan-Moss hat eine umfassende Beschreibung dynamischer Formularfelder: http://jacobian.org/writing/dynamic-form-generation/
Im Wesentlichen fügen Sie während der Instantiierung dem self.fields
-Wörterbuch des Formulars weitere Elemente hinzu.
Hier ist eine weitere Option: Wie wäre es mit einem formset ? Da Ihre Felder alle gleich sind, werden Formulare genau dafür verwendet.
Der Django-Administrator verwendet FormSet
s + ein wenig Javascript, um Inlines beliebiger Länge hinzuzufügen.
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" />
du kannst es gerne machen
def __init __ (self, n, * args, ** kwargs): super (your_form, self) .__ init__ (* args, ** kwargs) für i im Bereich (0, n): self.fields ["Feldname% d"% i] = forms.CharField ()
und wenn Sie eine Formularinstanz erstellen, tun Sie dies einfach
forms = Ihre_Form (n)
es ist nur die Grundidee, Sie können den Code beliebig ändern. : D
So würde ich es machen:
Erstellen Sie eine "leere" Klasse, die von froms.Form
erbt, wie folgt:
class ItemsForm(forms.Form):
pass
Erstellen Sie ein Wörterbuch mit Formularobjekten, bei denen es sich um die eigentlichen Formulare handelt, deren Zusammensetzung vom Kontext abhängig ist (z. B. können Sie sie aus einem externen Modul importieren). Zum Beispiel:
new_fields = {
'milk' : forms.IntegerField(),
'butter': forms.IntegerField(),
'honey' : forms.IntegerField(),
'eggs' : forms.IntegerField()}
In Ansichten können Sie die native "type" - Funktion von Python verwenden, um dynamisch eine Formularklasse mit variabler Anzahl von Feldern zu generieren.
DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
Übergeben Sie den Inhalt an das Formular und rendern Sie ihn in der Vorlage:
Form = DynamicItemsForm(content)
context['my_form'] = Form
return render(request, "demo/dynamic.html", context)
Der "Inhalt" ist ein Wörterbuch von Feldwerten (z. B. sogar request.POST würde das tun) . Sie können mein gesamtes Beispiel hier sehen.
Ein anderer Ansatz: Anstatt den normalen Feldinitialisierungsfluss zu unterbrechen, können Sie Felder mit einem Mixin überschreiben und ein OrderedDict von dynamischen Feldern in generate_dynamic_fields zurückgeben, die bei jeder Menge hinzugefügt werden.
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()
Ein einfaches Beispiel:
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