5

I have a pre_save defined on MyModel which looks something like this:

@receiver(pre_save, sender=MyModel)
def _mymodel_pre_save(sender, instance, **kwargs):
  if some_condition():
    instance.somecolumn = 'eggs'

i.e. it expects to be able to modify some attribute(s) of a MyModel instance, and of course expects those changes to be persisted during the save() call. I believe this is rather typical use of a pre_save function. This works fine as long as the save() call does not specify update_fields.

I am wondering if there is any safe and sensible way to use update_fields in a save() call of a MyModel instance at this point. If I naively call:

myinstance = MyModel.objects.get(id=100)
myinstance.othercolumn = 'spam'
myinstance.save(update_fields=['othercolumn'])

The UPDATE statement generated will look like:

UPDATE "myapp_mymodel" SET "othercolumn" = 'spam' WHERE "myapp_mymodel"."id" = 100

missing the intended update of "somecolumn" from the pre_save. I guess this condition can be detected from inside the pre_save by looking at the update_fields which are made available to the pre_save function (as a frozenset), but I can't see any way for a pre_save to force its intended changes to be made when the caller has a more-restrictive set of update_fields, as in the example above. Or is there any workaround?

Josh Kupershmidt
  • 2,540
  • 21
  • 30
  • 1
    The difference is that save can be either used for an update or a INSERT query, so you do have to alter the update_fields, I believe you could check in the pre_save signal if the update_fields is set, if it is then you could change and append? update_fields.append('somecolumn'). Haven't tried it, but you could check if this works. – petkostas Aug 04 '14 at 17:36
  • @petkostas No, as I mentioned in my summary, update_fields is made available to the pre_save function as a frozenset, i.e. it cannot be changed from there. – Josh Kupershmidt Aug 04 '14 at 18:01
  • Excellent question. As an alternative to the selected answer, another option is to prevent saving with `update_fields` by raising an exception as per https://stackoverflow.com/a/32729884/290182 – beldaz Aug 28 '22 at 21:13

1 Answers1

2

A work-around is:

@receiver(pre_save, sender=MyModel)
def _mymodel_pre_save(sender, instance, **kwargs):
  if some_condition():
    instance.somecolumn = 'eggs'
    instance.save()

Note the extra "instance.save()" call.

But you must ensure some_condition() is not True anymore after instance.somecolumn = 'eggs' has been executed. If not, it would result in a loop of save / pre-save / save / pre-save calls

Davy
  • 1,720
  • 1
  • 19
  • 42