Django Model Formset: only track changes to those items that have been updated/saved in the set?
Asked Answered
T

1

7

So, I'm using Django's Model Formset to produce sets of forms for different data. It is working great, but I want to add a feature where when a user displays the formset and, say, updates 2 out of the 10 items, I can track just the 2 updated, and output a message like "You have updated 2 items" kind-of-thing.

Do Django Model Formsets have a built in API for this? I can't seem to find it on the Django Docs.

I've tried various approaches but keep getting this when using the code offered by Peter below:

'Attendance' object has no attribute 'has_changed.' 

If I switch form.has_changed to formset.has_changed(), I get

'list' object has no attribute 'has_changed'

My View and Post method

class AttendanceView(TemplateView):

    template_name = 'example.html'

    def changed_forms(self, formset):
        return sum(1 for form in formset if form.has_changed())

def post(self, request, *args, **kwargs):
    formset = AttendanceFormSet(request.POST)
    if formset.is_valid():
        formset = formset.save()
        forms_changed = self.changed_forms(formset)
        context = self.get_context_data(**kwargs)
        context['total_changed_forms'] = forms_changed
        return self.render_to_response(context)
    else:
        return HttpResponse("POST failed")

So I figured it out, just change:

formset = formset.save() 

to

formset.save()
Teresetereshkova answered 13/6, 2016 at 18:25 Comment(0)
A
12

Formsets have a has_changed method which will report whether or not any of its forms have been changed. That's not exactly what you're looking for, but if you look at its implementation it should show you how to do it. That method is:

def has_changed(self):
    """
    Returns true if data in any form differs from initial.
    """
    return any(form.has_changed() for form in self)

So you can count changed forms with:

def changed_forms(formset):
    return sum(1 for form in formset if form.has_changed())

Or if you're comfortable using the integer meanings of boolean values:

    return sum(form.has_changed() for form in formset)

I personally find that unappealing compared to the more explicit mapping from true to 1, but opinions differ there.

Alper answered 13/6, 2016 at 18:42 Comment(7)
thanks @peter DeGlopper...how could I add that data to my template's context? If I want to output that data in my template?Teresetereshkova
Are you familiar with adding data to a context in general? This wouldn't be very different from that. It's obviously only possible after binding the formset to POST data.Alper
Hey Peter, I keep getting errors. I updated my code examples above. Doesn't look like my form is picking up the has_changed() method...Teresetereshkova
Do it before the save, or use a different variable name here so that formset still refers to the formset, not a list of instances: formset = formset.save()Alper
Hey Peter, so I got it working, just needed to change formset = formset.save() to formset.save()Teresetereshkova
Is there any way to check which fields have been changed?Koroseal
@Miind - at the per-form level, yes, as described here: stackoverflow.com/a/43550210Alper

© 2022 - 2024 — McMap. All rights reserved.