Using modelformset_factory in a Django Class Based View
Asked Answered
M

1

9

I am building a view that will let me update multiple fields on multiple objects at the same time. I'm doing this using ModelFormSet & modelformset_factory.

The template will be a table of forms with the object name to the left of the fields (see image below).

enter image description here

I found this example, but I am stuck on how to implement the class based view & template.

My Formset

class BaseFormSet(BaseModelFormSet):
    def __init__(self, *args, **kwargs):
        super(BaseFormSet, self).__init__(*args, **kwargs)
        self.queryset = Reference.objects.filter(
            start__isnull=True)


ReferenceFormSet = modelformset_factory(
    Reference,
    fields=('start', 'end'),
    formset=BaseFormSet,
    extra=0)

My View

class ReferenceFormSetView(LoginRequiredMixin, SuperuserRequiredMixin, FormView):
    model = Reference
    form_class = ReferenceFormSet
    template_name = "references/references_form.html"

    def form_valid(self, form):
        for sub_form in form:
            if sub_form.has_changed():
                sub_form.save()

        return super(ReferenceFormSetView, self).form_valid(form)

My Template

{% extends "base.html" %}
{% load crispy_forms_tags %}

{% block content %}
<div class="container">
    <h1>{{ headline }}</h1>
    <div class="row">
        <form action="" method="post">
            {% crispy form %}
            <div class="">
                <input type="submit" value="Submit" />
            </div>
        </form>
    </div>
</div>
{% endblock content %}

Questions

  • The view seems odd with the Formset in the form_class. Is there a better way to handle this?
  • How can I access the instance name to display in the form?
Mountainside answered 4/4, 2018 at 0:21 Comment(0)
M
6

I found a solution using a package called django-extra-views.

There is a class called ModelFormSetView which does exactly what I wanted. Here is my implementation (simplified) for others to use -

My View

class ReferenceFormSetView(ModelFormSetView):
    model = Reference
    template_name = "references/references_form.html"
    fields = ['start', 'end']
    extra = 0

    def get_queryset(self):
        return self.model.objects.all()

    def get_success_url(self):
        return reverse('references:formset')

    def formset_valid(self, formset):
        """
        If the formset is valid redirect to the supplied URL
        """
        messages.success(self.request, "Updated")
        return HttpResponseRedirect(self.get_success_url())

    def formset_invalid(self, formset):
        """
        If the formset is invalid, re-render the context data with the
        data-filled formset and errors.
        """
        messages.error(self.request, "Error dummy")
        return self.render_to_response(self.get_context_data(formset=formset))

My Template

<form class="" method="post">
    {% csrf_token %}
    {{ formset.management_form }}
    {% for form in formset %}
        <div class="">
            {% for field in form %}
                {{ field }}
            {% endfor %}
        </div>
    {% endfor %}
    <button type="submit" class="btn btn-primary">Save</button>
</form>
Mountainside answered 4/4, 2018 at 15:34 Comment(1)
Thank you for this django package suggestion. I hope it will soon gets some more coverage in its testing suite.Ivon

© 2022 - 2024 — McMap. All rights reserved.