Thu 06 August 2015

Filed under Django

Working with Formsets in Django

This was the specs:
- Form that is populated with rows of students: their first and last names. And the teacher then has to enter her comments about each student. Row by row, rather than having a separate form for each student. (Imagine an excel spreadsheet).

The solution was to use formsets; essentially each record is its own form.

To setup the formset:

In models:

class Student(models.Model):
    firstname = models.CharField(max_length=30, blank=False)
    lastname = models.CharField(max_length=30, blank=False)
    comments = models.TextField()

This is a standard Model.

In views:

from django.forms.models import modelformset_factory

StudentFormset = modelformset_factory(
        fields=('id', 'lastname', 'firstname', 'comments'),
student_list = Student.objects.all().order_by('lastname', 'firstname')

student_formset = StudentFormset(queryset= student_list)

context = {'formset': student_formset }
template_name = 'students/comments.html'
return render(request, template_name, context)
  1. First, import the modelformset_factory

  2. Then, I create my StudentFormset.

    • The basic syntax is:modelformset_factory(model, fields=(a tuple of fields from that model)). There are many more arguments you might use – like widgets, labels, error_messages. See the docs)
    • fields: I am basing this formset on the Student model, and listing exactly which fields from that model I want.
    • extra: how many blank forms I want. In this case, I don't want any, so it is 0.
  3. A formset can have several blank forms (you can determine how many). Or it can be prepopulated. In this case I needed a specific queryset. Hence I did my student_list queryset which I will soon pass to the formset (I could have also written it directly into the instance of the StudentFormset - below)

  4. I create an instance of the StudentFormset with the student_list queryset.

  5. I add the formset to the context to be rendered below.

the students/comments.html template

<form method="post">{% csrf_token %}
    {{ formset.management_form }}
      {% for form in formset %}
             <td>{{form.lastname}} {{ form.firstname }}  {{}} </td>
             <td>{{form.comments}} </td>
      {% endfor %}

    <button type="submit" name="save" >Save</button>

That line of the management_form is super important. It holds vital data about the formset – how many forms were actually created, etc. – the prefix (formset) must match the arguments used in the context. The rest is basic template with a for statement.

To save the data on the formset:

In the views:

if request.POST:
    formset = StudentFormset(request.POST)
    if formset.is_valid():

This is the most basic way to save. Normally, we might do other validations, etc. - Create an instance of the StudentFormset with the request.POST. - Make sure the data in all the forms in the formset it is valid. And then save. If anything is not valid, it will not save the entire formset. And produce a dictionary of errors.

Comments © Dee Kras Powered by Pelican and Twitter Bootstrap. Icons by Font Awesome and Font Awesome More