Package that helps update only changed fields

116 views
Skip to first unread message

Dan Davis

unread,
Jun 18, 2019, 11:38:53 AM6/18/19
to Django users
So, I recently observed that a Django ModelForm updates all columns when it is updating a model instance. This is appropriate for a typical situation where Django is also in control of the schema.
I also see that you can control this behavior by passing update_fields to the save method, in a particular form:

    self.instance.save(update_fields=self.changed_data)

I have some complicated forms that have non-model fields and many-to-many fields mixed in there.   I'm wondering whether there is a module for Django out there that gives me some base ModelForms that do not update all fields, but only changed fields.  I am asking because I don't want to work hard to handle corner cases if there's something already out there.   What I already have is hard-coded to the form where I've made this change to do only a limited update.

Thanks,

-Dan

Simon Charette

unread,
Jun 18, 2019, 1:01:06 PM6/18/19
to Django users
Hello Dan,

I'm not aware of any third party library handling this but a simple way to achieve
what you're after would be to subclass ModelForm in order to override save()
and pass commit=False in your super() call.

e.g. (untested)

class UpdateFieldsModelForm(ModelForm):
    update_fields = None

    def save(self, commit=True):
        instance = super().save(commit=False)
        if commit:
            update_fields = None if instance._state.adding else self.update_fields
            instance.save(update_fields=self.update_fields)
        return instance

Cheers,
Simon

Dan Davis

unread,
Jun 24, 2019, 10:41:42 PM6/24/19
to Django users
Thanks, I wrote a mixin class for most of my forms that works a lot like this, although it combines self.changed_fields and self.model._meta.fields to discover which fields to update, rather than the two stages you have above.  

I discovered when trying to generalize this mixin that some of my forms are too complicated for this to be a part of Django itself.  A ModelForm may have non-model fields whose initial value is computed from other models in __init__ or using a queryset with annotations.  I've found this to be easier in many cases that dealing with formsets.

So, if your save method already looks something like this:

    def save(self, commit=True):
        with transactions.atomic():
            instance = super().save()
            instance.relation.fubar = self.changed_data['relation_fubar']
            instance.relation.save()

Well, you might as well just figure out update_fields in that form on its own.

Reply all
Reply to author
Forward
0 new messages