To deal with all the issues expressed in various existing answers, I suggest the following approach: make a method, call it say saving
or modifying
, that is a context manager. The entry to that method sets a private flag which says the modification is in progress; the exit resets the flags and performs the saving; all modifying methods check the flag and raise an exception if not set. For example, using a base class and a save
method that real subclasses must override:
import contextlib
class CarefullyDesigned(object):
def __init__(self):
self.__saving = False
def _save(self):
raise NotImplementedError('Must override `_save`!')
def _checksaving(self):
"Call at start of subclass `save` and modifying-methods"
if not self.__saving: raise ValueError('No saving in progress!')
@contextlib.contextmanager
def saving(self):
if self.__saving: raise ValueError('Saving already in progress!')
self.__saving = True
yield
self._save()
self.__saving = False
Example use...:
class Bar(models.Model, CarefullyDesigned):
def __init__(self, *a, **k):
models.Model.__init__(self, *a, **k)
CarefullyDesigned.__init__(self)
def _save(self):
self._checksaving()
self.save()
def set_foo(self, foo):
self._checksaving()
self.foo = foo
def set_fie(self, fie):
self._checksaving()
self.fie = fie
bar = Bar()
with bar.saving():
bar.set_foo("foobar")
bar.set_fie("fo fum")
This guarantees the user won't forget to call saving
nor accidentally call it in a nested way (that's the purpose of all of those exceptions), and call save
only once when the group of modifying-methods is done, in a handy and, I'd say, pretty natural way.