BoJack Horseman
BoJack Horseman

Reputation: 4452

Dynamically adding new fields to a form in Django

I want to let my users write content and I want them to specify how many sections with its titles they want to have. I already created a little Javascript function to insert the new elements into the existing form which will be submitted. This currently looks like this:

openTextarea.js

var i = 1;

function opentextarea() {
  "use strict";
  var hr = document.createElement('hr');
  var title = document.createElement('input');
  title.type = 'text';
  title.name = 'title_'+i;
  title.placeholder = 'Your section title!';
  title.className = 'form-control';
  var input = document.createElement('textarea');
  input.name = 'section_'+i;
  input.className = 'tiny-mce-init';
  var button = document.createElement('button');
  var newSection = document.getElementById('newSection');
  var form = document.getElementById('guide_form');
  form.appendChild(hr);
  form.appendChild(title);
  form.appendChild(input);
  loadTinyMCEEditor();
  i++;
}

template

{% extends "base.html" %}

{% load staticfiles %}
{% block body %}

<script type="text/javascript" src="{% static "tinymce/js/tinymce/tinymce.min.js" %}"></script>
<script type="text/javascript" src="{% static "javascripts/openTextarea.js" %}"></script>
<script type="text/javascript" src="{% static "javascripts/loadTinyMCEEditor.js" %}"></script>

<div style="width: 60%; margin: 0 auto;">

  <hr>

  <form id="guide_form" class="form-horizontal" method="POST" action="{{ request.get_full_path }}"> {% csrf_token %}

    <fieldset>
      {% if form.title.errors %}
        <div class="class-group error">
          <div class="controls">{{ field }}
            <span class="help-inline">
              {% for error in form.title.errors %}{{ error }}{% endfor %}
            </span>
          </div>
        </div>
      {% else %}
        <div class="control-group">
          <div class="controls">{{ form.title }}</div>
        </div>
      {% endif %}

    <hr>

      {% if form.public.errors %}
        <div class="class-group error">
          <div class="controls">Check, if you want to make your guide public {{ field }}
            <span class="help-inline">
              {% for error in form.public.errors %}{{ error }}{% endfor %}
            </span>
          </div>
        </div>
      {% else %}
        <div class="control-group">
          <div class="controls">Check, if you want to make your guide public {{ form.public }}</div>
        </div>
      {% endif %}

    <hr>

    Neutral cards

    <hr>

    {% for card in form.cards %}
      {{ card }}
    {% endfor %}

    <hr>

    </fieldset>
  </form>

<hr>

<div id="newSection"></div>
<button class="btn btn-primary" type="button"
  onclick="opentextarea();">
  Add Section!
</button>

<hr>

<div class="form-actions" style="margin-top: 4px;">
  <button type="submit" form="guide_form" class="btn btn-success">Submit</button>
</div>

</div>

{% endblock %}

When I inspect the element, the new section appears in the form.

enter image description here

Apparently the new sections is not in the form.cleaned_data because when I print it out, I just receive the fields I initially created with my form:

class CreateGuide(FormView):
    template_name = 'hsguides/guide_create.html'
    form_class = GuideForm
    success_url = '/guides/search-guide/'

    def get_form_kwargs(self, *args, **kwargs):
        form_kwargs = super(CreateGuide, self).get_form_kwargs(**kwargs)
        form_kwargs['hero'] = self.kwargs['hero']
        return form_kwargs

    def form_valid(self, form, *args, **kwargs):
        # TODO
        # - check if cards are actually from selected class...
        # - implement sections
        form.instance.author = self.request.user
        form.instance.hero = Hero.objects.get(name=self.kwargs['hero'])
        dust = 0
        for card in form.cleaned_data['cards']:
            dust += Card.objects.get(id=card.id).dust

        for foo in form.cleaned_data:
            print(foo)

        form.instance.dust = dust
        form.save()
        return super(CreateGuide, self).form_valid(form)

The results are

cards

public

title

Which makes sense because my form looks like this

class GuideForm(BootstrapModelForm):
    class Meta:
        model = Guide
        exclude = ('author', 'created', 'modified', 'hero', 'dust', 'rating',)

    def __init__(self, *args, **kwargs):
        hero = kwargs.pop('hero', None)
        super(GuideForm, self).__init__(*args, **kwargs)
        if hero:
            self.fields['cards'].queryset = Card.objects.filter(Q(card_class='neutral') |
                                                                Q(card_class=hero))
        self.fields['title'].widget.attrs['placeholder'] = 'Title of your awesome guide!'
        self.fields['public'].widget.attrs.update({'class': 'checkbox'})

How can one dynamically add new elements into a form in django? Any help or suggestions are highly appreciated!

Upvotes: 3

Views: 7133

Answers (1)

Chillar Anand
Chillar Anand

Reputation: 29514

You can add them after calling super.

class GuideForm(BootstrapModelForm):
    class Meta:
        model = Guide
        exclude = ('author', 'created', 'modified', 'hero', 'dust', 'rating',)

    def __init__(self, *args, **kwargs):
        hero = kwargs.pop('hero', None)
        super(GuideForm, self).__init__(*args, **kwargs)

        #  add your fields here
        self.fields['title_1'] = forms.CharField()

You can check out this good example https://stackoverflow.com/a/6142749/2698552

Upvotes: 4

Related Questions