djangobootstrap-4django-crispy-formsformsetform-helpers

Django crispy forms - bootstrap4 table_inline_formset template rendering extra row on top


I am using the bootstrap4/table_inline_formset.html template in a FormHelper from django-crispy-forms. The table is rendered correctly in the template, but an extra form always appears at the beginning of the table, which is not visible when submitting the form.

forms.py:

class MetricForm(forms.ModelForm):
    class Meta:
        model = Metric
        exclude = ['auto_value','occurrence']

class MetricFormSetHelper(FormHelper):
    def __init__(self, *args, **kwargs):
        super(MetricFormSetHelper, self).__init__(*args, **kwargs)
        self.add_input(Submit('submit', 'Submit', css_class="btn btn-success"))
        self.template = 'bootstrap4/table_inline_formset.html'

views.py:

@login_required
def create_occurrence(request, pk):
    try:
        site = Site.objects.get(id=pk)
    except Site.DoesNotExist:
        raise Http404("Site does not exist")
    form = OccurrenceForm(request.POST or None, initial={'site':site})
    MetricFormset = modelformset_factory(Metric, form=MetricForm, extra=3)
    formset = MetricFormset(queryset=Metric.objects.none())
    helper = MetricFormSetHelper()
    if form.is_valid():
        occurrence = form.save(commit=False)
        occurrence.added_by = request.user
        occurrence.site = site
        occurrence.save()
        form.save_m2m()
        metric_formset  = MetricFormset(request.POST)
        if metric_formset.is_valid():
            for metric_form in metric_formset.forms:
                if all([metric_form.is_valid(), metric_form.cleaned_data != {}]):
                    metric = metric_form.save(commit=False)
                    metric.occurrence = occurrence
                    metric.save()
        messages.success(request, "Occurrence created successfully.")
        execute_from_command_line(["../manage_dev.sh", "updatelayers", "-s", "archaeology"])
        return redirect(occurrence.get_absolute_url())
    context = {
        'form': form,
        'site':site,
        'formset':formset,
        'helper': helper,
    }
    return render(request, "archaeology/occurrence_form.html", context=context)

template:

...
<form action="" method="post">
        {% csrf_token %}
        {{ form|crispy }}
        <h4>Metrics</h4>
        {{ formset.management_form }}
        {% crispy formset helper %}
        {% if form.instance.pk != None %}
        <a class="btn btn-danger" href="{% url 'delete_occurrence' occurrence.id %}">{% trans "Delete" %}</a>
        {% endif %}
    </form>
...

Any idea how to remove the extra row?


Solution

  • I had to change the template and remove the lines that printed an empty form at the beginning.

    table_inline_formset.html:

    <tr class="d-none empty-form">
        {% for field in formset.empty_form %}
            {% include 'bootstrap4/field.html' with tag="td" form_show_labels=False %}
        {% endfor %}
    </tr>