Frage Fügen Sie ein Feld dynamisch zu einem Formular hinzu


Ich habe 3 Felder in meiner Form. Ich habe eine Absenden-Schaltfläche und eine Schaltfläche "Zusätzliches Feld hinzufügen". Ich verstehe, dass ich Felder hinzufügen kann __init__ Methode in der Formularklasse.

Ich bin neu bei Python und Django und stecke mit einer Anfängerfrage fest: Meine Frage ist:

Wenn ich auf die Schaltfläche "Zusätzliches Feld hinzufügen" klicke, wie wird das zusätzliche Feld hinzugefügt?

Muss das Formular erneut gerendert werden?

Wie und wann rufe ich an? __init__ oder muss ich es sogar anrufen?

Wie übergebe ich Argumente __init__?


47
2018-05-26 16:38


Ursprung


Antworten:


Ihr Formular müsste basierend auf einigen Variablen erstellt werden, die Sie von Ihrem POST erhalten haben (oder blind nach Attributen suchen). Das Formular selbst wird jedes Mal erstellt, wenn die Ansicht neu geladen wird, Fehler oder nicht, also muss der HTML-Code Informationen darüber enthalten, wie viele Felder vorhanden sind, um die korrekte Anzahl von Feldern für die Validierung zu erstellen.

Ich würde dieses Problem auf die Art betrachten FormSets Arbeit: Es gibt ein verstecktes Feld, das die Anzahl der aktiven Formulare enthält, und jedem Formularnamen wird der Formularindex vorangestellt.

In der Tat könnten Sie ein einziges Feld machen FormSet

https://docs.djangoproject.com/en/dev/topics/forms/formsets/#formsets

Wenn Sie nicht a FormSet Sie können dieses Verhalten immer selbst erstellen.

Hier ist eine von Grund auf neu - es sollte Ihnen einige Ideen geben. Es beantwortet auch Ihre Fragen zum Übergeben von Argumenten an __init__ - Sie übergeben nur Argumente an einen Objektkonstruktor: MyForm('arg1', 'arg2', kwarg1='keyword arg')

Formen

class MyForm(forms.Form):
    original_field = forms.CharField()
    extra_field_count = forms.CharField(widget=forms.HiddenInput())

    def __init__(self, *args, **kwargs):
        extra_fields = kwargs.pop('extra', 0)

        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['extra_field_count'].initial = extra_fields

        for index in range(int(extra_fields)):
            # generate extra fields in the number specified via extra_fields
            self.fields['extra_field_{index}'.format(index=index)] = \
                forms.CharField()

Aussicht

def myview(request):
    if request.method == 'POST':
        form = MyForm(request.POST, extra=request.POST.get('extra_field_count'))
        if form.is_valid():
            print "valid!"
    else:
        form = MyForm()
    return render(request, "template", { 'form': form })

HTML

<form>
    <div id="forms">
        {{ form.as_p }}
    </div>
    <button id="add-another">add another</button>
    <input type="submit" />
</form>

JS

<script>
form_count = Number($("[name=extra_field_count]").val());
// get extra form count so we know what index to use for the next item.

$("#add-another").click(function() {
    form_count ++;

    element = $('<input type="text"/>');
    element.attr('name', 'extra_field_' + form_count);
    $("#forms").append(element);
    // build element and append it to our forms container

    $("[name=extra_field_count]").val(form_count);
    // increment form count so our view knows to populate 
    // that many fields for validation
})
</script>

53
2018-05-26 17:38



Ich hatte einen Fall, als ich dynamisch dynamische Formulare erstellen musste. Das habe ich mit diesem Trick gemacht:

from django import forms

...

dyn_form = type('DynForm',  # form name is irrelevant
                (forms.BaseForm,),
                {'base_fields': fields})

Weitere Informationen finden Sie unter diesem Link: Dynamische Formulare

Aber zusätzlich musste ich auch Felder injizieren, d. H. Dynamisch Felder zu einer Formularklasse hinzufügen, sobald sie erstellt wurde.

dyn_form.base_fields['field1'] = forms.IntegerField(widget=forms.HiddenInput(), initial=field1_val)
dyn_form.base_fields['field2'] = forms.CharField(widget=forms.HiddenInput(), initial=field2_val)

Und das hat funktioniert.


9
2017-12-17 01:11



Diese Antwort basiert auf @ Yuji'Tomita'Tomita mit einigen Verbesserungen und Änderungen.

Obwohl @ Yuji'Tomita'Tomita Antwort ist großartig und illustriert schön und einfach die Richtung zu folgen, um die "add extra Feld in einer Django-Form" -Funktion zu bauen, fand ich, dass es einige Probleme mit einigen Teilen des Codes gibt.

Hier stelle ich meinen Arbeitscode basierend auf dem ursprünglichen Vorschlag von @ Yuji'Tomita'Tomita zur Verfügung:

Ansichten  (in der Datei view.py)

In den Ansichten ändert sich nichts wirklich:

def myview(request):

  if request.method == 'POST':

    form = MyForm(request.POST, extra=request.POST.get('total_input_fields'))

      if form.is_valid():
        print "valid!"
      else:
        form = MyForm()
return render(request, "template", { 'form': form })

Bilden (in der Datei "form.py")

class MyForm(forms.Form):

    empty_layer_name = forms.CharField(max_length=255, required=True, label="Name of new Layer")

    total_input_fields = forms.CharField(widget=forms.HiddenInput())


    def __init__(self, *args, **kwargs):

      extra_fields = kwargs.pop('extra', 0)

      # check if extra_fields exist. If they don't exist assign 0 to them
      if not extra_fields:
         extra_fields = 0

      super(MyForm, self).__init__(*args, **kwargs)
      self.fields['total_input_fields'].initial = extra_fields

      for index in range(int(extra_fields)):
        # generate extra fields in the number specified via extra_fields
        self.fields['extra_field_{index}'.format(index=index)] = forms.CharField()

HTML-Vorlage

<form id="empty-layer-uploader" method="post" enctype="multipart/form-data" action="{% url "layer_create" %}">
        <div id="form_empty_layer">
          <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
            {{ form.errors }}
            {{ form.non_field_errors }}
            {% if errormsgs %}
              {% for value in errormsgs %}
                </p>  {{ value }} </p>
              {% endfor %}
            {% endif %}
            {% for error in form_empty_layer.non_field_errors %}
              {{ error }} </br>
            {% endfor %}
            </br>
            {% for field in form_empty_layer.visible_fields %}
              {{ field }} </br>
            {% endfor %}
        </div>
        </br>
        <button type="button" id="add-another">add another</button> </br> </br>
        <button type="submit" id="empty-layer-button" name="emptylayerbtn">Upload</button>
        </br></br>
        // used in order to save the number of added fields (this number will pass to forms.py through the view)
        <input type="text" name="total_input_fields"/>
</form>

Vorlage Jquery

// check how many times elements with this name attribute exist: extra_field_*
form_count = $('input[name*="extra_field_*"]').length;

// when the button 'add another' is clicked then create a new input element
$(document.body).on("click", "#add-another",function(e) {
  new_attribute = $('<input type="text"/>');
  // add a name attribute with a corresponding number (form_count)
  new_attribute.attr('name', 'extra_field_' + form_count);
  // append the new element in your html
  $("#form_empty_layer").append(new_attribute);
  // increment the form_count variable
  form_count ++;
  // save the form_count to another input element (you can set this to invisible. This is what you will pass to the form in order to create the django form fields
  $("[name=total_input_fields]").val(form_count);

})

4
2018-06-30 08:35



Ein Weg ohne Javascript und der Feldtyp ist in der js nicht beschrieben:

PYTHON

 def __init__(self, *args, **kwargs):
        super(Form, self).__init__(*args, **kwargs)

        ##ajouts des champs pour chaque chien
        for index in range(int(nb_dogs)):
            self.fields.update({
                'dog_%s_name' % index: forms.CharField(label=_('Name'), required=False, max_length=512),
            })

 def fields_dogs(self):
        fields = []
        for index in range(int(nb_dogs)):
            fields.append({
                'name': self['dog_%s_name' % index],
            })
        return fields

VORLAGE

{% for field_dog in f.fields_dogs %}
        <thead>
            <tr>
                <th style="background-color: #fff; border-width: 0px;"></th>
                <th>{% trans 'Dog' %} #{{forloop.counter}}</th>
                <th>{% trans 'Name' %}</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td style="background-color: #fff; border-width: 0px;"></td>
                <td style="background-color: #fff; border-width: 0px;"></td>
                <td>{{field_dog.name.errors}}{{field_dog.name}}</td>
            </tr>
            <tr>
                <td style="padding: 10px; border-width: 0px;"></td>
            </tr>
        </tbody>
{% endfor %}

3
2017-07-21 11:52



Yuji 'Tomita' Tomitas Lösung ist die beste Lösung, die Sie finden werden, aber vorausgesetzt, Sie haben ein Formular mit mehreren Schritten und Sie verwenden die django-formtools App, dann werden Sie einige Probleme haben, auf die Sie achten müssen. Danke Yuji 'Tomita' Tomita, du hast mir sehr geholfen :)

form.py

class LicmodelForm1(forms.Form):
     othercolumsvalue = forms.IntegerField(min_value=0, initial=0)
class LicmodelForm2(forms.Form):
    def __init__(self, *args, **kwargs):
    extra_fields = kwargs.pop('extra', 0)

    super(LicmodelForm2, self).__init__(*args, **kwargs)

    for index in range(int(extra_fields)):
        # generate extra fields in the number specified via extra_fields
        self.fields['othercolums_{index}'.format(index=index)] = \
            forms.CharField()
        self.fields['othercolums_{index}_nullable'.format(index=index)] = \
            forms.BooleanField(required=False)

Bei einem mehrstufigen Formular benötigen Sie in diesem Code kein zusätzliches Feld andererColumvalueFeld im ersten Schritt.

ansichten.py

class MyFormTool(SessionWizardView):
def get_template_names(self):
    return [TEMPLATES[self.steps.current]]

def get_context_data(self, form, **kwargs):
    context = super(MyFormTool, self).get_context_data(form=form, **kwargs)
    data_step1 = self.get_cleaned_data_for_step('step1')
    if self.steps.current == 'step2':

        #prepare tableparts for the needLists
        needList_counter = 0
        for i in self.wellKnownColums:
            if data_step1[i] is True:
                needList_counter = needList_counter + 1
                pass

        #prepare tableparts for othercolums
        othercolums_count = []
        for i in range(0, data_step1['othercolumsvalue']):
            othercolums_count.append(str(i))

        context.update({'step1': data_step1})
        context.update({'othercolums_count': othercolums_count})

    return context

def get_form(self, step=None, data=None, files=None):
    form = super(MyFormTool, self).get_form(step, data, files)

    if step is None:
        step = self.steps.current

    if step == 'step2':
        data = self.get_cleaned_data_for_step('step1')
        if data['othercolumsvalue'] is not 0:
            form = LicmodelForm2(self.request.POST,
                                 extra=data['othercolumsvalue'])
    return form

def done(self, form_list, **kwargs):
    print('done')
    return render(self.request, 'formtools_done.html', {
        'form_data' : [form.cleaned_data for form in form_list],
        })

Durch das Überschreiben der get_form () und get_context_data () Funktionen können Sie das Formular überschreiben, bevor es gerendert wird. Sie benötigen kein JavaScript mehr für Ihre Template-Datei:

            {% if step1.othercolumsvalue > 0 %}
            <tr>
                <th>Checkbox</th>
                <th>Columname</th>
            </tr>
            {% for i in othercolums_count %}
                <tr>
                    <td><center><input type="checkbox" name="othercolums_{{ i }}_nullable" id="id_othercolums_{{ i }}_nullable" /></center></td>
                    <td><center><input type="text" name="othercolums_{{ i }}" required id="id_othercolums_{{ i }}" /></center></td>
                </tr>
            {% endfor %}
        {% endif %}

Die Felder aus Schritt 2, die dynamisch erstellt wurden, wurden ebenfalls aus den Formtools wegen des gleichen Namens erkannt. Aber um dorthin zu gelangen, müssen Sie die For-Each-Template-Schleifen umgehen, wie Sie sehen können:

von der get_context_data () - Funktion

        othercolums_count = []
        for i in range(0, data_step1['othercolumsvalue']):
            othercolums_count.append(str(i))

0
2017-08-01 13:03